Adding ThreadPool (doesnt work)
This commit is contained in:
@@ -39,7 +39,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_SOURCE_DIR}/bi
|
|||||||
find_package(Vulkan REQUIRED)
|
find_package(Vulkan REQUIRED)
|
||||||
find_package(assimp CONFIG REQUIRED)
|
find_package(assimp CONFIG REQUIRED)
|
||||||
find_package(Stb REQUIRED)
|
find_package(Stb REQUIRED)
|
||||||
find_package(thread-pool CONFIG REQUIRED)
|
|
||||||
find_package(EnTT CONFIG REQUIRED)
|
find_package(EnTT CONFIG REQUIRED)
|
||||||
find_package(FreeType CONFIG REQUIRED)
|
find_package(FreeType CONFIG REQUIRED)
|
||||||
find_package(glfw3 CONFIG REQUIRED)
|
find_package(glfw3 CONFIG REQUIRED)
|
||||||
@@ -67,7 +66,6 @@ target_link_libraries(Engine PUBLIC freetype)
|
|||||||
target_link_libraries(Engine PUBLIC assimp::assimp)
|
target_link_libraries(Engine PUBLIC assimp::assimp)
|
||||||
target_link_libraries(Engine PUBLIC KTX::ktx)
|
target_link_libraries(Engine PUBLIC KTX::ktx)
|
||||||
target_link_libraries(Engine PUBLIC nlohmann_json::nlohmann_json)
|
target_link_libraries(Engine PUBLIC nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(Engine PUBLIC dp::thread-pool)
|
|
||||||
target_link_libraries(Engine PUBLIC crcpp)
|
target_link_libraries(Engine PUBLIC crcpp)
|
||||||
target_link_libraries(Engine PUBLIC shader-slang)
|
target_link_libraries(Engine PUBLIC shader-slang)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <thread_pool/thread_pool.h>
|
#include "ThreadPool.h"
|
||||||
#include "Window/View.h"
|
#include "Window/View.h"
|
||||||
#include "Graphics/RenderPass/DepthPrepass.h"
|
#include "Graphics/RenderPass/DepthPrepass.h"
|
||||||
#include "Graphics/RenderPass/LightCullingPass.h"
|
#include "Graphics/RenderPass/LightCullingPass.h"
|
||||||
@@ -34,7 +34,7 @@ private:
|
|||||||
LightCullingPass,
|
LightCullingPass,
|
||||||
BasePass> renderGraph;
|
BasePass> renderGraph;
|
||||||
|
|
||||||
dp::thread_pool<> pool;
|
ThreadPool pool;
|
||||||
ViewportControl cameraSystem;
|
ViewportControl cameraSystem;
|
||||||
|
|
||||||
virtual void keyCallback(KeyCode code, InputAction action, KeyModifier modifier) override;
|
virtual void keyCallback(KeyCode code, InputAction action, KeyModifier modifier) override;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ target_sources(Engine
|
|||||||
Concepts.h
|
Concepts.h
|
||||||
EngineTypes.h
|
EngineTypes.h
|
||||||
Game.h
|
Game.h
|
||||||
MinimalEngine.h)
|
MinimalEngine.h
|
||||||
|
ThreadPool.h
|
||||||
|
ThreadPool.cpp)
|
||||||
|
|
||||||
target_sources(Engine
|
target_sources(Engine
|
||||||
PUBLIC FILE_SET HEADERS
|
PUBLIC FILE_SET HEADERS
|
||||||
@@ -11,7 +13,8 @@ target_sources(Engine
|
|||||||
Concepts.h
|
Concepts.h
|
||||||
Game.h
|
Game.h
|
||||||
EngineTypes.h
|
EngineTypes.h
|
||||||
MinimalEngine.h)
|
MinimalEngine.h
|
||||||
|
ThreadPool.h)
|
||||||
|
|
||||||
add_subdirectory(Actor/)
|
add_subdirectory(Actor/)
|
||||||
add_subdirectory(Asset/)
|
add_subdirectory(Asset/)
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ public:
|
|||||||
, _size(std::move(other._size))
|
, _size(std::move(other._size))
|
||||||
, allocator(std::move(other.allocator))
|
, allocator(std::move(other.allocator))
|
||||||
{
|
{
|
||||||
|
other._size = 0;
|
||||||
}
|
}
|
||||||
List(List&& other, const Allocator& alloc)
|
List(List&& other, const Allocator& alloc)
|
||||||
: root(std::move(other.root))
|
: root(std::move(other.root))
|
||||||
@@ -188,7 +189,7 @@ public:
|
|||||||
, _size(std::move(other._size))
|
, _size(std::move(other._size))
|
||||||
, allocator(allocator)
|
, allocator(allocator)
|
||||||
{
|
{
|
||||||
other.clear();
|
other._size = 0;
|
||||||
}
|
}
|
||||||
~List()
|
~List()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ constexpr static uint64 NUM_DEFAULT_ELEMENTS = 1024 * 1024;
|
|||||||
|
|
||||||
void VertexData::resetMeshData()
|
void VertexData::resetMeshData()
|
||||||
{
|
{
|
||||||
|
std::unique_lock l(materialDataLock);
|
||||||
for (auto& [_, mat] : materialData)
|
for (auto& [_, mat] : materialData)
|
||||||
{
|
{
|
||||||
mat.material->getDescriptorLayout()->reset();
|
mat.material->getDescriptorLayout()->reset();
|
||||||
@@ -28,11 +29,12 @@ void VertexData::resetMeshData()
|
|||||||
|
|
||||||
void VertexData::updateMesh(PMesh mesh, Component::Transform& transform)
|
void VertexData::updateMesh(PMesh mesh, Component::Transform& transform)
|
||||||
{
|
{
|
||||||
|
std::unique_lock l(materialDataLock);
|
||||||
PMaterial mat = mesh->referencedMaterial->getHandle()->getBaseMaterial();
|
PMaterial mat = mesh->referencedMaterial->getHandle()->getBaseMaterial();
|
||||||
MaterialData& matData = materialData[mat->getName()];
|
MaterialData& matData = materialData[mat->getName()];
|
||||||
matData.material = mat;
|
matData.material = mat;
|
||||||
MaterialInstanceData& matInstanceData = matData.instances[mesh->referencedMaterial->getHandle()->getId()];
|
MaterialInstanceData& matInstanceData = matData.instances[mesh->referencedMaterial->getHandle()->getId()];
|
||||||
for (auto& data : meshData[mesh->id])
|
for (const auto& data : meshData[mesh->id])
|
||||||
{
|
{
|
||||||
matInstanceData.meshes.add(MeshInstanceData{
|
matInstanceData.meshes.add(MeshInstanceData{
|
||||||
.instance = InstanceData {
|
.instance = InstanceData {
|
||||||
@@ -46,6 +48,7 @@ void VertexData::updateMesh(PMesh mesh, Component::Transform& transform)
|
|||||||
|
|
||||||
void VertexData::createDescriptors()
|
void VertexData::createDescriptors()
|
||||||
{
|
{
|
||||||
|
std::unique_lock l(materialDataLock);
|
||||||
instanceDataLayout->reset();
|
instanceDataLayout->reset();
|
||||||
for (const auto& [_, mat] : materialData)
|
for (const auto& [_, mat] : materialData)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ protected:
|
|||||||
uint32_t vertexOffset;
|
uint32_t vertexOffset;
|
||||||
uint32_t primitiveOffset;
|
uint32_t primitiveOffset;
|
||||||
};
|
};
|
||||||
|
std::mutex materialDataLock;
|
||||||
Map<std::string, MaterialData> materialData;
|
Map<std::string, MaterialData> materialData;
|
||||||
Map<MeshId, Array<MeshData>> meshData;
|
Map<MeshId, Array<MeshData>> meshData;
|
||||||
Map<MeshId, uint64> meshOffsets;
|
Map<MeshId, uint64> meshOffsets;
|
||||||
|
|||||||
@@ -28,25 +28,29 @@ public:
|
|||||||
{
|
{
|
||||||
return (deps | ...);
|
return (deps | ...);
|
||||||
}
|
}
|
||||||
void setupView(Dependencies<>, dp::thread_pool<>&)
|
void setupView(Dependencies<>, ThreadPool& pool)
|
||||||
{
|
{
|
||||||
|
List<std::function<void()>> work;
|
||||||
registry.view<Components...>().each([&](Components&... comp){
|
registry.view<Components...>().each([&](Components&... comp){
|
||||||
//pool.enqueue_detach([&](){
|
work.add([&](){
|
||||||
update(comp...);
|
update(comp...);
|
||||||
//});
|
});
|
||||||
});
|
});
|
||||||
|
pool.runAndWait(std::move(work));
|
||||||
}
|
}
|
||||||
template<typename... Deps>
|
template<typename... Deps>
|
||||||
void setupView(Dependencies<Deps...>, dp::thread_pool<>&)
|
void setupView(Dependencies<Deps...>, ThreadPool& pool)
|
||||||
{
|
{
|
||||||
|
List<std::function<void()>> work;
|
||||||
registry.view<Components..., Deps...>().each([&](Components&... comp, Deps&... deps){
|
registry.view<Components..., Deps...>().each([&](Components&... comp, Deps&... deps){
|
||||||
//pool.enqueue_detach([&](){
|
work.add([&]() {
|
||||||
(accessComponent(deps) + ...);
|
(accessComponent(deps) + ...);
|
||||||
update((comp,...));
|
update((comp,...));
|
||||||
//});
|
});
|
||||||
});
|
});
|
||||||
|
pool.runAndWait(std::move(work));
|
||||||
}
|
}
|
||||||
virtual void run(dp::thread_pool<>& pool, double delta) override
|
virtual void run(ThreadPool& pool, double delta) override
|
||||||
{
|
{
|
||||||
SystemBase::run(pool, delta);
|
SystemBase::run(pool, delta);
|
||||||
setupView(mergeDependencies((getDependencies<Components>(),...)), pool);
|
setupView(mergeDependencies((getDependencies<Components>(),...)), pool);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "MinimalEngine.h"
|
#include "MinimalEngine.h"
|
||||||
#include "SystemBase.h"
|
#include "SystemBase.h"
|
||||||
#include <thread_pool/thread_pool.h>
|
|
||||||
|
|
||||||
namespace Seele
|
namespace Seele
|
||||||
{
|
{
|
||||||
@@ -13,7 +12,7 @@ public:
|
|||||||
Executor();
|
Executor();
|
||||||
~Executor();
|
~Executor();
|
||||||
private:
|
private:
|
||||||
dp::thread_pool<> pool;
|
ThreadPool pool;
|
||||||
};
|
};
|
||||||
} // namespace System
|
} // namespace System
|
||||||
} // namespace Seele
|
} // namespace Seele
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <thread_pool/thread_pool.h>
|
|
||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
|
#include "ThreadPool.h"
|
||||||
#include "Scene/Scene.h"
|
#include "Scene/Scene.h"
|
||||||
|
|
||||||
namespace Seele
|
namespace Seele
|
||||||
@@ -12,7 +12,7 @@ class SystemBase
|
|||||||
public:
|
public:
|
||||||
SystemBase(PScene scene) : registry(scene->registry), scene(scene) {}
|
SystemBase(PScene scene) : registry(scene->registry), scene(scene) {}
|
||||||
virtual ~SystemBase() {}
|
virtual ~SystemBase() {}
|
||||||
virtual void run(dp::thread_pool<>&, double delta)
|
virtual void run(ThreadPool& pool, double delta)
|
||||||
{
|
{
|
||||||
deltaTime = delta;
|
deltaTime = delta;
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ void SystemGraph::addSystem(System::OSystemBase system)
|
|||||||
systems.add(std::move(system));
|
systems.add(std::move(system));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemGraph::run(dp::thread_pool<>& threadPool, float deltaTime)
|
void SystemGraph::run(ThreadPool& threadPool, float deltaTime)
|
||||||
{
|
{
|
||||||
for(auto& system : systems) {
|
for(auto& system : systems) {
|
||||||
system->run(threadPool, deltaTime);
|
system->run(threadPool, deltaTime);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "ThreadPool.h"
|
||||||
#include "Containers/Array.h"
|
#include "Containers/Array.h"
|
||||||
#include "SystemBase.h"
|
#include "SystemBase.h"
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ class SystemGraph
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void addSystem(System::OSystemBase system);
|
void addSystem(System::OSystemBase system);
|
||||||
void run(dp::thread_pool<>& threadPool, float deltaTime);
|
void run(ThreadPool& threadPool, float deltaTime);
|
||||||
private:
|
private:
|
||||||
Array<System::OSystemBase> systems;
|
Array<System::OSystemBase> systems;
|
||||||
};
|
};
|
||||||
|
|||||||
+33
-191
@@ -1,224 +1,66 @@
|
|||||||
#include "ThreadPool.h"
|
#include "ThreadPool.h"
|
||||||
#include <memory_resource>
|
|
||||||
|
|
||||||
using namespace Seele;
|
using namespace Seele;
|
||||||
|
|
||||||
std::mutex Seele::promisesLock;
|
ThreadPool::ThreadPool(uint32 numWorkers)
|
||||||
List<Promise*> Seele::promises;
|
|
||||||
|
|
||||||
//Event::Event(nullptr_t)
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
Event::Event(const std::string &name, const std::source_location &location)
|
|
||||||
: state(std::make_shared<EventState>())
|
|
||||||
{
|
{
|
||||||
state->name = name;
|
for (uint32 i = 0; i < numWorkers; ++i)
|
||||||
state->location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Event(const std::source_location &location)
|
|
||||||
: state(std::make_shared<EventState>())
|
|
||||||
{
|
|
||||||
state->name = location.function_name();
|
|
||||||
state->location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Event(const Event& other)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(other.eventLock);
|
|
||||||
state = other.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Event(Event&& other)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(other.eventLock);
|
|
||||||
state = std::move(other.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
Event& Event::operator=(const Event& other)
|
|
||||||
{
|
|
||||||
if(this != &other)
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(eventLock, other.eventLock);
|
workers.add(std::thread(&ThreadPool::work, this));
|
||||||
state = other.state;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Event& Event::operator=(Event&& other)
|
|
||||||
{
|
|
||||||
if(this != &other)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(eventLock, other.eventLock);
|
|
||||||
state = std::move(other.state);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Event::raise()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(eventLock);
|
|
||||||
state->data = true;
|
|
||||||
if(state->waitingJobs.size() > 0)
|
|
||||||
{
|
|
||||||
getGlobalThreadPool().scheduleBatch(state->waitingJobs);
|
|
||||||
state->waitingJobs.clear();
|
|
||||||
}
|
|
||||||
if(state->waitingMainJobs.size() > 0)
|
|
||||||
{
|
|
||||||
getGlobalThreadPool().scheduleBatch(state->waitingMainJobs);
|
|
||||||
state->waitingMainJobs.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Event::reset()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(eventLock);
|
|
||||||
state->data = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Event::await_ready()
|
|
||||||
{
|
|
||||||
eventLock.lock();
|
|
||||||
bool result = state->data;
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
eventLock.unlock();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Event::await_suspend(std::coroutine_handle<JobPromiseBase<false>> h)
|
|
||||||
{
|
|
||||||
state->waitingJobs.add(JobBase<false>(&h.promise()));
|
|
||||||
eventLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Event::await_suspend(std::coroutine_handle<JobPromiseBase<true>> h)
|
|
||||||
{
|
|
||||||
state->waitingMainJobs.add(JobBase<true>(&h.promise()));
|
|
||||||
eventLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPool::ThreadPool(uint32 threadCount)
|
|
||||||
: workers(threadCount)
|
|
||||||
{
|
|
||||||
running.store(true);
|
|
||||||
for (uint32 i = 0; i < threadCount; ++i)
|
|
||||||
{
|
|
||||||
workers[i] = std::thread(&ThreadPool::threadLoop, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::~ThreadPool()
|
ThreadPool::~ThreadPool()
|
||||||
{
|
{
|
||||||
running.store(false);
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mainJobLock);
|
std::unique_lock l(taskLock);
|
||||||
mainJobCV.notify_all();
|
running = false;
|
||||||
|
taskCV.notify_all();
|
||||||
}
|
}
|
||||||
{
|
for (auto& worker : workers)
|
||||||
std::unique_lock lock(jobQueueLock);
|
|
||||||
jobQueueCV.notify_all();
|
|
||||||
}
|
|
||||||
for(auto& worker : workers)
|
|
||||||
{
|
{
|
||||||
worker.join();
|
worker.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::waitIdle()
|
void ThreadPool::runAndWait(List<std::function<void()>> functions)
|
||||||
{
|
{
|
||||||
while(true)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(numIdlingLock);
|
std::unique_lock l(taskLock);
|
||||||
if(numIdling == workers.size())
|
currentTask.numRemaining = functions.size();
|
||||||
{
|
currentTask.functions = std::move(functions);
|
||||||
assert(promises.size() == 0);
|
taskCV.notify_all();
|
||||||
return;
|
|
||||||
}
|
|
||||||
numIdlingIncr.wait(lock);
|
|
||||||
}
|
}
|
||||||
}
|
while (currentTask.numRemaining > 0)
|
||||||
void ThreadPool::scheduleJob(Job job)
|
|
||||||
{
|
|
||||||
assert(!job.done());
|
|
||||||
std::scoped_lock lock(jobQueueLock);
|
|
||||||
jobQueue.add(std::move(job));
|
|
||||||
jobQueueCV.notify_one();
|
|
||||||
}
|
|
||||||
void ThreadPool::scheduleJob(MainJob job)
|
|
||||||
{
|
|
||||||
assert(!job.done());
|
|
||||||
std::scoped_lock lock(mainJobLock);
|
|
||||||
mainJobs.add(std::move(job));
|
|
||||||
mainJobCV.notify_one();
|
|
||||||
}
|
|
||||||
void ThreadPool::mainLoop()
|
|
||||||
{
|
|
||||||
while(running.load())
|
|
||||||
{
|
{
|
||||||
MainJob job;
|
std::unique_lock l2(completedLock);
|
||||||
{
|
completedCV.wait(l2);
|
||||||
std::unique_lock lock(mainJobLock);
|
|
||||||
if(mainJobs.empty())
|
|
||||||
{
|
|
||||||
mainJobCV.wait(lock);
|
|
||||||
}
|
|
||||||
[[likely]]
|
|
||||||
if(!mainJobs.empty())
|
|
||||||
{
|
|
||||||
job = mainJobs.front();
|
|
||||||
mainJobs.popFront();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job.resume();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::threadLoop()
|
void ThreadPool::work()
|
||||||
{
|
{
|
||||||
List<Job> localQueue;
|
while (true)
|
||||||
while (running.load())
|
|
||||||
{
|
{
|
||||||
[[likely]]
|
std::unique_lock l(taskLock);
|
||||||
if(!localQueue.empty())
|
while(currentTask.functions.empty())
|
||||||
{
|
{
|
||||||
Job job = localQueue.retrieve();
|
taskCV.wait(l);
|
||||||
job.resume();
|
if (!running)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
auto func = std::move(currentTask.functions.front());
|
||||||
|
currentTask.functions.popFront();
|
||||||
|
l.unlock();
|
||||||
|
func();
|
||||||
|
l.lock();
|
||||||
|
currentTask.numRemaining--;
|
||||||
|
if (currentTask.numRemaining == 0)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(jobQueueLock);
|
std::unique_lock l2(completedLock);
|
||||||
if (jobQueue.empty())
|
completedCV.notify_one();
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock lock2(numIdlingLock);
|
|
||||||
numIdling++;
|
|
||||||
numIdlingIncr.notify_one();
|
|
||||||
}
|
|
||||||
jobQueueCV.wait(lock);
|
|
||||||
{
|
|
||||||
std::unique_lock lock2(numIdlingLock);
|
|
||||||
numIdling--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// take 1/numThreads jobs, maybe make this a parameter that
|
|
||||||
// adjusts based on past workload
|
|
||||||
uint32 partitionedWorkload = (uint32)(jobQueue.size() / workers.size());
|
|
||||||
uint32 numTaken = std::clamp(partitionedWorkload, 1u, localQueueSize);
|
|
||||||
while (!jobQueue.empty() && localQueue.size() < numTaken)
|
|
||||||
{
|
|
||||||
localQueue.add(jobQueue.retrieve());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadPool &Seele::getGlobalThreadPool()
|
|
||||||
{
|
|
||||||
static ThreadPool threadPool;
|
|
||||||
return threadPool;
|
|
||||||
}
|
|
||||||
+17
-447
@@ -1,459 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <coroutine>
|
#include "Containers/Array.h"
|
||||||
#include "MinimalEngine.h"
|
|
||||||
#include "Containers/List.h"
|
#include "Containers/List.h"
|
||||||
#include "Concepts.h"
|
|
||||||
#include <source_location>
|
|
||||||
|
|
||||||
namespace Seele
|
namespace Seele
|
||||||
{
|
{
|
||||||
extern class ThreadPool& getGlobalThreadPool();
|
|
||||||
template<bool MainJob>
|
|
||||||
struct JobBase;
|
|
||||||
template<bool MainJob>
|
|
||||||
struct JobPromiseBase;
|
|
||||||
struct Event
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//Event(nullptr_t);
|
|
||||||
Event(const std::string& name, const std::source_location& location = std::source_location::current());
|
|
||||||
Event(const std::source_location& location = std::source_location::current());
|
|
||||||
Event(const Event& other);
|
|
||||||
Event(Event&& other);
|
|
||||||
~Event() = default;
|
|
||||||
Event& operator=(const Event& other);
|
|
||||||
Event& operator=(Event&& other);
|
|
||||||
auto operator<=>(const Event& other) const
|
|
||||||
{
|
|
||||||
return state->name <=> other.state->name;
|
|
||||||
}
|
|
||||||
bool operator==(const Event& other) const
|
|
||||||
{
|
|
||||||
return state->name == other.state->name;
|
|
||||||
}
|
|
||||||
operator bool()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(eventLock);
|
|
||||||
return state->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const Event& event)
|
|
||||||
{
|
|
||||||
stream
|
|
||||||
<< event.state->location.file_name()
|
|
||||||
<< "("
|
|
||||||
<< event.state->location.line()
|
|
||||||
<< ":"
|
|
||||||
<< event.state->location.column()
|
|
||||||
<< "): "
|
|
||||||
<< event.state->location.function_name();
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
void raise();
|
|
||||||
void reset();
|
|
||||||
bool await_ready();
|
|
||||||
void await_suspend(std::coroutine_handle<JobPromiseBase<false>> h);
|
|
||||||
void await_suspend(std::coroutine_handle<JobPromiseBase<true>> h);
|
|
||||||
constexpr void await_resume() {}
|
|
||||||
private:
|
|
||||||
mutable std::mutex eventLock;
|
|
||||||
struct EventState
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::source_location location;
|
|
||||||
bool data = false;
|
|
||||||
Array<JobBase<false>> waitingJobs;
|
|
||||||
Array<JobBase<true>> waitingMainJobs;
|
|
||||||
};
|
|
||||||
std::shared_ptr<EventState> state;
|
|
||||||
friend class ThreadPool;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern std::mutex promisesLock;
|
|
||||||
extern List<JobPromiseBase<false>*> promises;
|
|
||||||
template<bool MainJob>
|
|
||||||
struct JobPromiseBase
|
|
||||||
{
|
|
||||||
enum class State
|
|
||||||
{
|
|
||||||
READY,
|
|
||||||
WAITING,
|
|
||||||
SCHEDULED,
|
|
||||||
EXECUTING,
|
|
||||||
DONE
|
|
||||||
};
|
|
||||||
JobPromiseBase(const std::source_location& location = std::source_location::current())
|
|
||||||
: pad0(12345)//0x7472617453)
|
|
||||||
, handle(std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this))
|
|
||||||
, waitingFor(nullptr)
|
|
||||||
, continuation(nullptr)
|
|
||||||
, numRefs(0)
|
|
||||||
, finishedEvent(location)
|
|
||||||
, state(State::READY)
|
|
||||||
, pad1(12345)//0x646E45)
|
|
||||||
{
|
|
||||||
if constexpr(!MainJob)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(promisesLock);
|
|
||||||
promises.add(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~JobPromiseBase()
|
|
||||||
{
|
|
||||||
if constexpr (!MainJob)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(promisesLock);
|
|
||||||
promises.remove(promises.find(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JobBase<MainJob> get_return_object() noexcept;
|
|
||||||
|
|
||||||
inline auto initial_suspend() noexcept;
|
|
||||||
inline auto final_suspend() noexcept;
|
|
||||||
|
|
||||||
void return_void() noexcept {}
|
|
||||||
void unhandled_exception() {
|
|
||||||
std::exception_ptr exception = std::current_exception();
|
|
||||||
std::cerr << "Unhandled exception!" << std::endl;
|
|
||||||
throw exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
void resume()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(promiseLock);
|
|
||||||
validate();
|
|
||||||
if(!handle || handle.done() || executing())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = State::EXECUTING;
|
|
||||||
handle.resume();
|
|
||||||
}
|
|
||||||
void setContinuation(JobPromiseBase* cont)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(promiseLock, cont->promiseLock);
|
|
||||||
validate();
|
|
||||||
assert(cont->ready());
|
|
||||||
continuation = cont;
|
|
||||||
cont->state = State::SCHEDULED;
|
|
||||||
cont->waitingFor = &finishedEvent;
|
|
||||||
cont->addRef();
|
|
||||||
}
|
|
||||||
bool done()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return state == State::DONE;
|
|
||||||
}
|
|
||||||
bool scheduled()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return state == State::SCHEDULED;
|
|
||||||
}
|
|
||||||
bool waiting()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return state == State::WAITING;
|
|
||||||
}
|
|
||||||
bool executing()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return state == State::EXECUTING;
|
|
||||||
}
|
|
||||||
bool ready()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return state == State::READY;
|
|
||||||
}
|
|
||||||
void enqueue(Event* event);
|
|
||||||
bool schedule();
|
|
||||||
void addRef()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
numRefs++;
|
|
||||||
}
|
|
||||||
void removeRef()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(promiseLock);
|
|
||||||
validate();
|
|
||||||
numRefs--;
|
|
||||||
if(numRefs != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(schedule())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle.destroy();
|
|
||||||
}
|
|
||||||
void validate()
|
|
||||||
{
|
|
||||||
assert(pad0 == 12345);
|
|
||||||
assert(pad1 == 12345);
|
|
||||||
}
|
|
||||||
uint64 pad0;
|
|
||||||
std::mutex promiseLock;
|
|
||||||
std::coroutine_handle<JobPromiseBase> handle;
|
|
||||||
Event* waitingFor;
|
|
||||||
JobPromiseBase* continuation;
|
|
||||||
std::atomic_uint64_t numRefs;
|
|
||||||
Event finishedEvent;
|
|
||||||
State state;
|
|
||||||
uint64 pad1;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<bool MainJob = false>
|
|
||||||
struct JobBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using promise_type = JobPromiseBase<MainJob>;
|
|
||||||
|
|
||||||
explicit JobBase()
|
|
||||||
: promise(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
explicit JobBase(JobPromiseBase<MainJob>* promise)
|
|
||||||
: promise(promise)
|
|
||||||
{
|
|
||||||
promise->addRef();
|
|
||||||
}
|
|
||||||
JobBase(const JobBase& other)
|
|
||||||
{
|
|
||||||
promise = other.promise;
|
|
||||||
if(promise != nullptr)
|
|
||||||
{
|
|
||||||
promise->addRef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobBase(JobBase&& other)
|
|
||||||
{
|
|
||||||
promise = other.promise;
|
|
||||||
other.promise = nullptr;
|
|
||||||
}
|
|
||||||
~JobBase()
|
|
||||||
{
|
|
||||||
if(promise)
|
|
||||||
{
|
|
||||||
//promise->schedule();
|
|
||||||
promise->removeRef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobBase& operator=(const JobBase& other)
|
|
||||||
{
|
|
||||||
if(this != &other)
|
|
||||||
{
|
|
||||||
if(promise != nullptr)
|
|
||||||
{
|
|
||||||
promise->removeRef();
|
|
||||||
}
|
|
||||||
promise = other.promise;
|
|
||||||
if(promise != nullptr)
|
|
||||||
{
|
|
||||||
promise->addRef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
JobBase& operator=(JobBase&& other)
|
|
||||||
{
|
|
||||||
if(this != &other)
|
|
||||||
{
|
|
||||||
if(promise != nullptr)
|
|
||||||
{
|
|
||||||
promise->removeRef();
|
|
||||||
}
|
|
||||||
promise = other.promise;
|
|
||||||
other.promise = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
void resume()
|
|
||||||
{
|
|
||||||
promise->resume();
|
|
||||||
}
|
|
||||||
template<std::invocable Callable>
|
|
||||||
inline JobBase then(Callable callable)
|
|
||||||
{
|
|
||||||
return then(callable());
|
|
||||||
}
|
|
||||||
JobBase then(JobBase continuation)
|
|
||||||
{
|
|
||||||
promise->setContinuation(continuation.promise);
|
|
||||||
continuation.promise->state = JobPromiseBase<MainJob>::State::SCHEDULED;
|
|
||||||
return continuation;
|
|
||||||
}
|
|
||||||
bool done()
|
|
||||||
{
|
|
||||||
return promise->done();
|
|
||||||
}
|
|
||||||
Event& operator co_await()
|
|
||||||
{
|
|
||||||
// the co_await operator keeps a reference to this, it won't
|
|
||||||
// be scheduled from the destructor
|
|
||||||
std::scoped_lock lock(promise->promiseLock);
|
|
||||||
promise->schedule();
|
|
||||||
return promise->finishedEvent;
|
|
||||||
}
|
|
||||||
static JobBase all() = delete;
|
|
||||||
|
|
||||||
template<std::ranges::range Iterable>
|
|
||||||
requires std::same_as<std::ranges::range_value_t<Iterable>, JobBase<MainJob>>
|
|
||||||
static JobBase<MainJob> all(Iterable collection);
|
|
||||||
|
|
||||||
template<std::ranges::range Iterable>
|
|
||||||
static JobBase all(Iterable collection)
|
|
||||||
{
|
|
||||||
for(auto it : collection)
|
|
||||||
{
|
|
||||||
co_await it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Awaitable>
|
|
||||||
static JobBase all(Awaitable... jobs)
|
|
||||||
{
|
|
||||||
std::vector vec{jobs...};
|
|
||||||
return std::move(JobBase::all(std::move(vec)));
|
|
||||||
}
|
|
||||||
template<typename JobFunc, std::ranges::input_range Iterable>
|
|
||||||
requires std::invocable<JobFunc, std::ranges::range_reference_t<Iterable>>
|
|
||||||
static JobBase launchJobs(JobFunc&& func, Iterable params);
|
|
||||||
private:
|
|
||||||
JobPromiseBase<MainJob>* promise;
|
|
||||||
friend class ThreadPool;
|
|
||||||
};
|
|
||||||
|
|
||||||
using MainJob = JobBase<true>;
|
|
||||||
using Job = JobBase<false>;
|
|
||||||
using MainPromise = JobPromiseBase<true>;
|
|
||||||
using Promise = JobPromiseBase<false>;
|
|
||||||
|
|
||||||
class ThreadPool
|
class ThreadPool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
|
ThreadPool(uint32 numWorkers = std::thread::hardware_concurrency());
|
||||||
virtual ~ThreadPool();
|
~ThreadPool();
|
||||||
void waitIdle();
|
void runAndWait(List<std::function<void()>> functions);
|
||||||
void scheduleJob(Job job);
|
|
||||||
void scheduleJob(MainJob job);
|
|
||||||
template<std::ranges::range Iterable>
|
|
||||||
requires std::same_as<std::ranges::range_value_t<Iterable>, MainJob>
|
|
||||||
void scheduleBatch(Iterable jobs)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(mainJobLock);
|
|
||||||
for(auto job : jobs)
|
|
||||||
{
|
|
||||||
std::scoped_lock l(job.promise->promiseLock);
|
|
||||||
job.promise->validate();
|
|
||||||
job.promise->state = JobPromiseBase<true>::State::SCHEDULED;
|
|
||||||
mainJobs.add(job);
|
|
||||||
}
|
|
||||||
mainJobCV.notify_one();
|
|
||||||
}
|
|
||||||
template<std::ranges::range Iterable>
|
|
||||||
requires std::same_as<std::ranges::range_value_t<Iterable>, Job>
|
|
||||||
void scheduleBatch(Iterable jobs)
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(jobQueueLock);
|
|
||||||
for(auto job : jobs)
|
|
||||||
{
|
|
||||||
std::scoped_lock l(job.promise->promiseLock);
|
|
||||||
job.promise->validate();
|
|
||||||
job.promise->state = JobPromiseBase<false>::State::SCHEDULED;
|
|
||||||
jobQueue.add(job);
|
|
||||||
}
|
|
||||||
jobQueueCV.notify_all();
|
|
||||||
}
|
|
||||||
void notify(Event* event);
|
|
||||||
void mainLoop();
|
|
||||||
void threadLoop();
|
|
||||||
private:
|
private:
|
||||||
std::atomic_bool running;
|
struct Task
|
||||||
std::mutex numIdlingLock;
|
{
|
||||||
std::condition_variable numIdlingIncr;
|
uint64 numRemaining = 0;
|
||||||
uint32 numIdling;
|
List<std::function<void()>> functions;
|
||||||
|
};
|
||||||
|
void work();
|
||||||
Array<std::thread> workers;
|
Array<std::thread> workers;
|
||||||
|
std::mutex taskLock;
|
||||||
List<MainJob> mainJobs;
|
std::condition_variable taskCV;
|
||||||
std::mutex mainJobLock;
|
std::mutex completedLock;
|
||||||
std::condition_variable mainJobCV;
|
std::condition_variable completedCV;
|
||||||
|
Task currentTask;
|
||||||
List<Job> jobQueue;
|
bool running = true;
|
||||||
std::mutex jobQueueLock;
|
|
||||||
std::condition_variable jobQueueCV;
|
|
||||||
|
|
||||||
uint32 localQueueSize = 50;
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
template<bool MainJob>
|
|
||||||
inline JobBase<MainJob> JobPromiseBase<MainJob>::get_return_object() noexcept
|
|
||||||
{
|
|
||||||
return JobBase<MainJob>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool MainJob>
|
|
||||||
inline auto JobPromiseBase<MainJob>::initial_suspend() noexcept
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
return std::suspend_always{};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool MainJob>
|
|
||||||
inline auto JobPromiseBase<MainJob>::final_suspend() noexcept
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
state = State::DONE;
|
|
||||||
finishedEvent.raise();
|
|
||||||
if(continuation)
|
|
||||||
{
|
|
||||||
getGlobalThreadPool().scheduleJob(JobBase<MainJob>(continuation));
|
|
||||||
continuation->removeRef();
|
|
||||||
}
|
|
||||||
return std::suspend_always{};
|
|
||||||
}
|
|
||||||
template<bool MainJob>
|
|
||||||
inline bool JobPromiseBase<MainJob>::schedule()
|
|
||||||
{
|
|
||||||
validate();
|
|
||||||
if(!handle || done() || !ready())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
state = State::SCHEDULED;
|
|
||||||
getGlobalThreadPool().scheduleJob(std::move(JobBase<MainJob>(this)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template<bool MainJob>
|
|
||||||
template<std::ranges::range Iterable>
|
|
||||||
requires std::same_as<std::ranges::range_value_t<Iterable>, JobBase<MainJob>>
|
|
||||||
inline JobBase<MainJob> JobBase<MainJob>::all(Iterable collection)
|
|
||||||
{
|
|
||||||
getGlobalThreadPool().scheduleBatch(collection);
|
|
||||||
for(auto it : collection)
|
|
||||||
{
|
|
||||||
co_await it;
|
|
||||||
}
|
|
||||||
collection.clear();
|
|
||||||
}
|
|
||||||
template<bool MainJob>
|
|
||||||
template<typename JobFunc, std::ranges::input_range Iterable>
|
|
||||||
requires std::invocable<JobFunc, std::ranges::range_reference_t<Iterable>>
|
|
||||||
inline JobBase<MainJob> JobBase<MainJob>::launchJobs(JobFunc&& func, Iterable params)
|
|
||||||
{
|
|
||||||
Array<JobBase<MainJob>> jobs;
|
|
||||||
for(auto&& param : params)
|
|
||||||
{
|
|
||||||
jobs.add(func(param));
|
|
||||||
}
|
|
||||||
getGlobalThreadPool().scheduleBatch(jobs);
|
|
||||||
for(auto job : jobs)
|
|
||||||
{
|
|
||||||
co_await job;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace Seele
|
|
||||||
@@ -40,7 +40,7 @@ private:
|
|||||||
|
|
||||||
PSystemGraph systemGraph;
|
PSystemGraph systemGraph;
|
||||||
System::PKeyboardInput keyboardSystem;
|
System::PKeyboardInput keyboardSystem;
|
||||||
dp::thread_pool<> threadPool;
|
ThreadPool threadPool;
|
||||||
float updateTime = 0;
|
float updateTime = 0;
|
||||||
|
|
||||||
virtual void keyCallback(Seele::KeyCode code, Seele::InputAction action, Seele::KeyModifier modifier) override;
|
virtual void keyCallback(Seele::KeyCode code, Seele::InputAction action, Seele::KeyModifier modifier) override;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"assimp",
|
"assimp",
|
||||||
"entt",
|
"entt",
|
||||||
"stb",
|
"stb",
|
||||||
"dp-thread-pool",
|
|
||||||
"entt",
|
"entt",
|
||||||
"freetype",
|
"freetype",
|
||||||
"glfw3",
|
"glfw3",
|
||||||
|
|||||||
Reference in New Issue
Block a user