From ef567bd2238dccf0cc35fd23b9cc02e6c9525943 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 3 Dec 2012 01:30:04 +0100 Subject: [PATCH] Added Task Scheduler Former-commit-id: ac37f749ae78f364db3d3edfabc8221802579989 --- include/Nazara/Core/TaskScheduler.hpp | 29 +++++ include/Nazara/Core/TaskScheduler.inl | 25 ++++ include/Nazara/Renderer/ShaderBuilder.hpp | 36 ++++++ src/Nazara/Core/TaskScheduler.cpp | 150 ++++++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 include/Nazara/Core/TaskScheduler.hpp create mode 100644 include/Nazara/Core/TaskScheduler.inl create mode 100644 include/Nazara/Renderer/ShaderBuilder.hpp create mode 100644 src/Nazara/Core/TaskScheduler.cpp diff --git a/include/Nazara/Core/TaskScheduler.hpp b/include/Nazara/Core/TaskScheduler.hpp new file mode 100644 index 000000000..5f7c80537 --- /dev/null +++ b/include/Nazara/Core/TaskScheduler.hpp @@ -0,0 +1,29 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_TASKSCHEDULER_HPP +#define NAZARA_TASKSCHEDULER_HPP + +#include +#include + +class NAZARA_API NzTaskScheduler +{ + public: + template static void AddTask(F function); + template static void AddTask(F function, Args... args); + template static void AddTask(void (C::*function)(), C* object); + static bool Initialize(); + static void Uninitialize(); + static void WaitForTasks(); + + private: + static void AddTaskFunctor(NzFunctor* taskFunctor); +}; + +#include + +#endif // NAZARA_TASKSCHEDULER_HPP diff --git a/include/Nazara/Core/TaskScheduler.inl b/include/Nazara/Core/TaskScheduler.inl new file mode 100644 index 000000000..da4450184 --- /dev/null +++ b/include/Nazara/Core/TaskScheduler.inl @@ -0,0 +1,25 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +template +void NzTaskScheduler::AddTask(F function) +{ + AddTaskFunctor(new NzFunctorWithoutArgs(function)); +} + +template +void NzTaskScheduler::AddTask(F function, Args... args) +{ + AddTaskFunctor(new NzFunctorWithArgs(function, args...)); +} + +template +void NzTaskScheduler::AddTask(void (C::*function)(), C* object) +{ + AddTaskFunctor(new NzMemberWithoutArgs(function, object)); +} + +#include diff --git a/include/Nazara/Renderer/ShaderBuilder.hpp b/include/Nazara/Renderer/ShaderBuilder.hpp new file mode 100644 index 000000000..2c9cb15ed --- /dev/null +++ b/include/Nazara/Renderer/ShaderBuilder.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADERBUILDER_HPP +#define NAZARA_SHADERBUILDER_HPP + +#include +#include + +class NAZARA_API NzShaderBuilder +{ + public: + NzShaderBuilder() = default; + NzShaderBuilder(nzShaderLanguage language); + ~NzShaderBuilder(); + + bool Create(nzShaderLanguage language); + void Destroy(); + + bool IsValid(); + + void SetDeferred(bool deferredRendering); + void SetDiffuseMapping(bool diffuseMapping); + void SetInstancing(bool instancing); + void SetLighting(bool lighting); + void SetNormalMapping(bool normalMapping); + void SetParallaxMapping(bool parallaxMapping); + + private: + NzShaderImpl* m_impl = nullptr; +}; + +#endif // NAZARA_SHADER_HPP diff --git a/src/Nazara/Core/TaskScheduler.cpp b/src/Nazara/Core/TaskScheduler.cpp new file mode 100644 index 000000000..6ac70d8ed --- /dev/null +++ b/src/Nazara/Core/TaskScheduler.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + struct TaskSchedulerImpl + { + std::queue tasks; + std::vector workers; + NzConditionVariable waiterConditionVariable; + NzConditionVariable workerConditionVariable; + NzMutex taskMutex; + NzMutex waiterConditionVariableMutex; + NzMutex workerConditionVariableMutex; + volatile bool running = true; + }; + + TaskSchedulerImpl* s_impl = nullptr; + + void WorkerFunc() + { + do + { + NzFunctor* task; + { + NzLockGuard lock(s_impl->taskMutex); + if (!s_impl->tasks.empty()) + { + task = s_impl->tasks.front(); + s_impl->tasks.pop(); + } + else + task = nullptr; + } + + // Avons-nous une tâche ? + if (task) + task->Run(); // Chouette ! Allons travailler gaiement + else + { + // On peut signaler à tout le monde qu'il n'y a plus de tâches + s_impl->waiterConditionVariableMutex.Lock(); + s_impl->waiterConditionVariable.SignalAll(); + s_impl->waiterConditionVariableMutex.Unlock(); + + // Dans le cas contraire, nous attendons qu'une nouvelle tâche arrive + s_impl->workerConditionVariableMutex.Lock(); + s_impl->workerConditionVariable.Wait(&s_impl->workerConditionVariableMutex); + s_impl->workerConditionVariableMutex.Unlock(); + } + } + while (s_impl->running); + } +} + +bool NzTaskScheduler::Initialize() +{ + if (s_impl) + return true; // Déjà initialisé + + s_impl = new TaskSchedulerImpl; + + unsigned int workerCount = NzThread::HardwareConcurrency(); + for (unsigned int i = 0; i < workerCount; ++i) + { + NzThread* thread = new NzThread(WorkerFunc); + s_impl->workers.push_back(thread); + } + + return true; +} + +void NzTaskScheduler::Uninitialize() +{ + if (s_impl) + { + s_impl->running = false; + + s_impl->workerConditionVariableMutex.Lock(); + s_impl->workerConditionVariable.SignalAll(); + s_impl->workerConditionVariableMutex.Unlock(); + + for (NzThread* thread : s_impl->workers) + { + thread->Join(); + delete thread; + } + + delete s_impl; + } +} + +void NzTaskScheduler::AddTaskFunctor(NzFunctor* taskFunctor) +{ + #ifdef NAZARA_CORE_SAFE + if (!s_impl) + { + NazaraError("Task scheduler is not initialized"); + return; + } + #endif + + { + NzLockGuard lock(s_impl->taskMutex); + s_impl->tasks.push(taskFunctor); + } + + s_impl->workerConditionVariableMutex.Lock(); + s_impl->workerConditionVariable.Signal(); + s_impl->workerConditionVariableMutex.Unlock(); +} + +void NzTaskScheduler::WaitForTasks() +{ + #ifdef NAZARA_CORE_SAFE + if (!s_impl) + { + NazaraError("Task scheduler is not initialized"); + return; + } + #endif + + s_impl->taskMutex.Lock(); + // Tout d'abord, il y a-t-il des tâches en attente ? + if (s_impl->tasks.empty()) + { + s_impl->taskMutex.Unlock(); + return; + } + + // On verrouille d'abord la mutex entourant le signal (Pour ne pas perdre le signal en chemin) + s_impl->waiterConditionVariableMutex.Lock(); + // Et ensuite seulement on déverrouille la mutex des tâches + s_impl->taskMutex.Unlock(); + s_impl->waiterConditionVariable.Wait(&s_impl->waiterConditionVariableMutex); + s_impl->waiterConditionVariableMutex.Unlock(); +}