Somewhat fixed thread pool
This commit is contained in:
@@ -75,6 +75,7 @@ MainJob LightCullingPass::beginFrame()
|
||||
lightEnvDescriptorSet->updateBuffer(2, pointLightBuffer);
|
||||
lightEnvDescriptorSet->updateBuffer(3, numPointLightBuffer);
|
||||
lightEnvDescriptorSet->writeChanges();
|
||||
std::cout << "Finished light culling beginFrame()" << std::endl;
|
||||
co_return;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ void CmdBuffer::end()
|
||||
|
||||
void CmdBuffer::beginRenderPass(PRenderPass newRenderPass, PFramebuffer newFramebuffer)
|
||||
{
|
||||
assert(state == State::InsideBegin);
|
||||
std::unique_lock lock(handleLock);
|
||||
renderPass = newRenderPass;
|
||||
framebuffer = newFramebuffer;
|
||||
@@ -87,6 +88,7 @@ void CmdBuffer::executeCommands(const Array<Gfx::PRenderCommand>& commands)
|
||||
assert(state == State::RenderPassActive);
|
||||
if(commands.size() == 0)
|
||||
{
|
||||
std::cout << "No commands!" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::unique_lock lock(handleLock);
|
||||
|
||||
+88
-76
@@ -3,19 +3,20 @@
|
||||
|
||||
using namespace Seele;
|
||||
|
||||
|
||||
Event::Event()
|
||||
: flag(new std::atomic_bool())
|
||||
{}
|
||||
: flag(new std::atomic_bool())
|
||||
{
|
||||
}
|
||||
|
||||
Event::Event(nullptr_t)
|
||||
: flag(nullptr)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
Event::Event(const std::string& name)
|
||||
: name(name)
|
||||
, flag(new std::atomic_bool())
|
||||
{}
|
||||
Event::Event(const std::string &name)
|
||||
: name(name), flag(new std::atomic_bool())
|
||||
{
|
||||
}
|
||||
|
||||
void Event::raise()
|
||||
{
|
||||
@@ -23,7 +24,7 @@ void Event::raise()
|
||||
getGlobalThreadPool().notify(*this);
|
||||
}
|
||||
void Event::reset()
|
||||
{
|
||||
{
|
||||
flag->store(0);
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ ThreadPool::ThreadPool(uint32 threadCount)
|
||||
: workers(threadCount)
|
||||
{
|
||||
running.store(true);
|
||||
for(uint32 i = 0; i < threadCount; ++i)
|
||||
for (uint32 i = 0; i < threadCount; ++i)
|
||||
{
|
||||
workers[i] = std::thread(&ThreadPool::threadLoop, this, false);
|
||||
}
|
||||
@@ -45,48 +46,52 @@ ThreadPool::ThreadPool(uint32 threadCount)
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
running.store(false);
|
||||
for(auto& thread : workers)
|
||||
for (auto &thread : workers)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
void ThreadPool::addJob(Job&& job)
|
||||
void ThreadPool::enqueueWaiting(Event &event, Promise* job)
|
||||
{
|
||||
std::unique_lock lock(jobQueueLock);
|
||||
//std::cout << "Adding job " << job.id << std::endl;
|
||||
jobQueue.add(std::move(job));
|
||||
jobQueueCV.notify_one();
|
||||
assert(!job->done());
|
||||
if(event == nullptr)
|
||||
{
|
||||
std::unique_lock lock(jobQueueLock);
|
||||
jobQueue.add(job);
|
||||
jobQueueCV.notify_one();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock lock(waitingLock);
|
||||
waitingJobs[event].add(job);
|
||||
}
|
||||
}
|
||||
void ThreadPool::addJob(MainJob&& job)
|
||||
void ThreadPool::enqueueWaiting(Event &event, MainPromise* job)
|
||||
{
|
||||
std::unique_lock lock(mainJobLock);
|
||||
//std::cout << "Adding main job " << job.id << std::endl;
|
||||
mainJobs.add(std::move(job));
|
||||
mainJobCV.notify_one();
|
||||
assert(!job->done());
|
||||
if(event == nullptr)
|
||||
{
|
||||
std::unique_lock lock(mainJobLock);
|
||||
mainJobs.add(job);
|
||||
mainJobCV.notify_one();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock lock(waitingMainLock);
|
||||
waitingMainJobs[event].add(job);
|
||||
}
|
||||
}
|
||||
void ThreadPool::enqueueWaiting(Event& event, Job&& job)
|
||||
{
|
||||
//std::cout << job.id << " waiting for event " << event.name << std::endl;
|
||||
std::unique_lock lock(waitingLock);
|
||||
waitingJobs[event].add(std::move(job));
|
||||
}
|
||||
void ThreadPool::enqueueWaiting(Event& event, MainJob&& job)
|
||||
{
|
||||
//std::cout << job.id << " main waiting for event " << event.name << std::endl;
|
||||
std::unique_lock lock(waitingMainLock);
|
||||
waitingMainJobs[event].add(std::move(job));
|
||||
}
|
||||
void ThreadPool::notify(Event& event)
|
||||
void ThreadPool::notify(Event &event)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(jobQueueLock);
|
||||
std::unique_lock lock2(waitingLock);
|
||||
List<Job>& jobs = waitingJobs[event];
|
||||
for(auto& job : jobs)
|
||||
List<Promise*> &jobs = waitingJobs[event];
|
||||
for (auto &job : jobs)
|
||||
{
|
||||
//assert(job.id != -1ull);
|
||||
//std::cout << "Waking up job " << job.id << std::endl;
|
||||
jobQueue.add(std::move(job));
|
||||
jobQueue.add(job);
|
||||
jobQueueCV.notify_one();
|
||||
}
|
||||
jobs.clear();
|
||||
@@ -94,67 +99,74 @@ void ThreadPool::notify(Event& event)
|
||||
{
|
||||
std::unique_lock lock(mainJobLock);
|
||||
std::unique_lock lock2(waitingMainLock);
|
||||
List<MainJob>& jobs = waitingMainJobs[event];
|
||||
for(auto& job : jobs)
|
||||
List<MainPromise*> &jobs = waitingMainJobs[event];
|
||||
for (auto &job : jobs)
|
||||
{
|
||||
//assert(job.id != -1ull);
|
||||
//std::cout << "Waking up main job " << job.id << std::endl;
|
||||
mainJobs.add(std::move(job));
|
||||
mainJobs.add(job);
|
||||
mainJobCV.notify_one();
|
||||
}
|
||||
jobs.clear();
|
||||
}
|
||||
}
|
||||
void ThreadPool::tryMainJob()
|
||||
void ThreadPool::threadLoop(const bool mainThread)
|
||||
{
|
||||
MainJob job;
|
||||
while (running.load())
|
||||
{
|
||||
std::unique_lock lock(mainJobLock);
|
||||
if(!mainJobs.empty())
|
||||
if (mainThread)
|
||||
{
|
||||
job = std::move(mainJobs.retrieve());
|
||||
MainPromise* job;
|
||||
{
|
||||
std::unique_lock lock(mainJobLock);
|
||||
if(mainJobs.empty())
|
||||
{
|
||||
mainJobCV.wait(lock);
|
||||
}
|
||||
if (!mainJobs.empty())
|
||||
{
|
||||
job = mainJobs.retrieve();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
job->resume();
|
||||
if (job->done())
|
||||
{
|
||||
job->raise();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
job.resume();
|
||||
}
|
||||
void ThreadPool::threadLoop(const bool mainThread)
|
||||
{
|
||||
while(running.load())
|
||||
{
|
||||
if(mainThread)
|
||||
{
|
||||
tryMainJob();
|
||||
}
|
||||
Job job;
|
||||
{
|
||||
std::unique_lock lock(jobQueueLock);
|
||||
if(jobQueue.empty())
|
||||
Promise* job;
|
||||
{
|
||||
jobQueueCV.wait(lock);
|
||||
std::unique_lock lock(jobQueueLock);
|
||||
if (jobQueue.empty())
|
||||
{
|
||||
jobQueueCV.wait(lock);
|
||||
}
|
||||
if (!jobQueue.empty())
|
||||
{
|
||||
job = jobQueue.retrieve();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!jobQueue.empty())
|
||||
//std::cout << "Starting job " << job.id << std::endl;
|
||||
job->resume();
|
||||
if (job->done())
|
||||
{
|
||||
job = std::move(jobQueue.retrieve());
|
||||
job->raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//std::cout << "Starting job " << job.id << std::endl;
|
||||
job.resume();
|
||||
if(job.done())
|
||||
{
|
||||
job.raise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool& Seele::getGlobalThreadPool()
|
||||
ThreadPool &Seele::getGlobalThreadPool()
|
||||
{
|
||||
static ThreadPool threadPool;
|
||||
return threadPool;
|
||||
|
||||
+128
-98
@@ -8,29 +8,8 @@
|
||||
namespace Seele
|
||||
{
|
||||
extern class ThreadPool& getGlobalThreadPool();
|
||||
|
||||
template<bool MainJob>
|
||||
struct JobBase;
|
||||
struct Event;
|
||||
static std::atomic_uint64_t globalCounter;
|
||||
template<bool MainJob>
|
||||
struct JobPromiseBase
|
||||
{
|
||||
JobBase<MainJob> get_return_object() noexcept;
|
||||
|
||||
inline auto initial_suspend() noexcept;
|
||||
inline auto final_suspend() const noexcept;
|
||||
|
||||
void return_void() noexcept {}
|
||||
void unhandled_exception() noexcept {
|
||||
std::cerr << "Unhandled exception" << std::endl;
|
||||
exit(1);
|
||||
};
|
||||
std::coroutine_handle<> continuation = std::noop_coroutine();
|
||||
uint64 id;
|
||||
Event finishedEvent;
|
||||
};
|
||||
|
||||
struct JobPromiseBase;
|
||||
struct Event
|
||||
{
|
||||
public:
|
||||
@@ -41,6 +20,10 @@ public:
|
||||
{
|
||||
return flag <=> other.flag;
|
||||
}
|
||||
bool operator==(const Event& other) const
|
||||
{
|
||||
return flag == other.flag;
|
||||
}
|
||||
Event operator co_await()
|
||||
{
|
||||
return *this;
|
||||
@@ -62,65 +45,43 @@ private:
|
||||
friend class ThreadPool;
|
||||
};
|
||||
|
||||
template<bool MainJob = false>
|
||||
struct JobBase
|
||||
static std::atomic_uint64_t globalCounter;
|
||||
template<bool MainJob>
|
||||
struct JobBase;
|
||||
template<bool MainJob>
|
||||
struct JobPromiseBase
|
||||
{
|
||||
public:
|
||||
using promise_type = JobPromiseBase<MainJob>;
|
||||
|
||||
explicit JobBase()
|
||||
: id(-1)
|
||||
JobPromiseBase()
|
||||
{
|
||||
handle = std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this);
|
||||
id = globalCounter++;
|
||||
finishedEvent = Event(std::format("Job {}", id));
|
||||
}
|
||||
explicit JobBase(std::coroutine_handle<promise_type> handle, uint64 id, Event finishedEvent)
|
||||
: handle(handle)
|
||||
, event(std::move(finishedEvent))
|
||||
, id(id)
|
||||
{
|
||||
}
|
||||
JobBase(const JobBase & rhs) = delete;
|
||||
JobBase(JobBase&& rhs)
|
||||
: handle(std::move(rhs.handle))
|
||||
, event(rhs.event)
|
||||
, id(std::move(rhs.id))
|
||||
{
|
||||
rhs.id = -1;
|
||||
rhs.handle = nullptr;
|
||||
}
|
||||
~JobBase()
|
||||
{
|
||||
if(handle && handle.done())
|
||||
{
|
||||
std::cout << "Destroy job " << handle.address() << std::endl;
|
||||
handle.destroy();
|
||||
}
|
||||
}
|
||||
JobBase& operator=(const JobBase& rhs) = delete;
|
||||
JobBase& operator=(JobBase&& rhs)
|
||||
{
|
||||
if(this != &rhs)
|
||||
{
|
||||
handle = std::move(rhs.handle);
|
||||
event = std::move(rhs.event);
|
||||
id = std::move(rhs.id);
|
||||
rhs.id = -1;
|
||||
rhs.handle = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
JobBase<MainJob> get_return_object() noexcept;
|
||||
|
||||
inline auto initial_suspend() noexcept;
|
||||
inline auto final_suspend() const noexcept;
|
||||
|
||||
void return_void() noexcept {}
|
||||
void unhandled_exception() noexcept {
|
||||
std::cerr << "Unhandled exception" << std::endl;
|
||||
exit(1);
|
||||
};
|
||||
|
||||
void resume()
|
||||
{
|
||||
handle.resume();
|
||||
std::unique_lock lock(promiseLock);
|
||||
if(handle && !handle.done())
|
||||
{
|
||||
handle.resume();
|
||||
}
|
||||
}
|
||||
template<std::invocable Callable>
|
||||
inline JobBase then(Callable callable)
|
||||
void setContinuation(JobPromiseBase* cont)
|
||||
{
|
||||
return then(callable());
|
||||
}
|
||||
JobBase then(JobBase continuation)
|
||||
{
|
||||
handle.promise().continuation = continuation.handle;
|
||||
return continuation;
|
||||
std::unique_lock lock(promiseLock);
|
||||
std::unique_lock lock2(cont->promiseLock);
|
||||
continuation = cont->handle;
|
||||
cont->precondition = finishedEvent;
|
||||
}
|
||||
bool done()
|
||||
{
|
||||
@@ -128,15 +89,87 @@ public:
|
||||
}
|
||||
void raise()
|
||||
{
|
||||
event.raise();
|
||||
std::unique_lock lock(promiseLock);
|
||||
finishedEvent.raise();
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
event.reset();
|
||||
std::unique_lock lock(promiseLock);
|
||||
finishedEvent.reset();
|
||||
}
|
||||
void enqueue(Event& event)
|
||||
{
|
||||
getGlobalThreadPool().enqueueWaiting(event, this);
|
||||
}
|
||||
void schedule()
|
||||
{
|
||||
std::unique_lock lock(promiseLock);
|
||||
if(!handle || handle.done())
|
||||
{
|
||||
return;
|
||||
}
|
||||
getGlobalThreadPool().enqueueWaiting(precondition, this);
|
||||
}
|
||||
|
||||
std::mutex promiseLock;
|
||||
std::coroutine_handle<JobPromiseBase> handle;
|
||||
std::coroutine_handle<> continuation = std::noop_coroutine();
|
||||
uint64 id;
|
||||
Event finishedEvent;
|
||||
Event precondition = nullptr;
|
||||
};
|
||||
|
||||
template<bool MainJob = false>
|
||||
struct JobBase
|
||||
{
|
||||
public:
|
||||
using promise_type = JobPromiseBase<MainJob>;
|
||||
|
||||
explicit JobBase()
|
||||
: promise(nullptr)
|
||||
{
|
||||
}
|
||||
explicit JobBase(JobPromiseBase<MainJob>* promise)
|
||||
: promise(promise)
|
||||
{
|
||||
}
|
||||
~JobBase()
|
||||
{
|
||||
if(promise)
|
||||
{
|
||||
promise->schedule();
|
||||
}
|
||||
}
|
||||
void resume()
|
||||
{
|
||||
promise->resume();
|
||||
}
|
||||
template<std::invocable Callable>
|
||||
inline JobBase then(Callable callable)
|
||||
{
|
||||
return then(callable());
|
||||
}
|
||||
JobBase then(JobBase&& continuation)
|
||||
{
|
||||
promise->setContinuation(continuation.promise);
|
||||
return continuation;
|
||||
}
|
||||
bool done()
|
||||
{
|
||||
return promise->done();
|
||||
}
|
||||
void raise()
|
||||
{
|
||||
promise->raise();
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
promise->reset();
|
||||
}
|
||||
Event operator co_await()
|
||||
{
|
||||
return event;
|
||||
promise->resume();
|
||||
return promise->finishedEvent;
|
||||
}
|
||||
static JobBase all() = delete;
|
||||
template<typename... Awaitable>
|
||||
@@ -166,58 +199,54 @@ public:
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::coroutine_handle<promise_type> handle;
|
||||
Event event;
|
||||
uint64 id;
|
||||
JobPromiseBase<MainJob>* promise;
|
||||
};
|
||||
|
||||
using MainJob = JobBase<true>;
|
||||
using Job = JobBase<false>;
|
||||
using MainPromise = JobPromiseBase<true>;
|
||||
using Promise = JobPromiseBase<false>;
|
||||
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
|
||||
virtual ~ThreadPool();
|
||||
void addJob(Job&& job);
|
||||
void addJob(MainJob&& job);
|
||||
void enqueueWaiting(Event& event, Job&& job);
|
||||
void enqueueWaiting(Event& event, MainJob&& job);
|
||||
// Adds a job to the waiting queue for event, or directly to the job queue if event is nullptr
|
||||
void enqueueWaiting(Event& event, Promise* job);
|
||||
// Adds a job to the waiting queue for event, or directly to the job queue if event is nullptr
|
||||
void enqueueWaiting(Event& event, MainPromise* job);
|
||||
void notify(Event& event);
|
||||
void threadLoop(const bool isMainThread);
|
||||
private:
|
||||
std::atomic_bool running;
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
List<MainJob> mainJobs;
|
||||
List<MainPromise*> mainJobs;
|
||||
std::mutex mainJobLock;
|
||||
std::condition_variable mainJobCV;
|
||||
|
||||
List<Job> jobQueue;
|
||||
List<Promise*> jobQueue;
|
||||
std::mutex jobQueueLock;
|
||||
std::condition_variable jobQueueCV;
|
||||
|
||||
Map<Event, List<Job>> waitingJobs;
|
||||
std::mutex waitingLock;
|
||||
|
||||
Map<Event, List<MainJob>> waitingMainJobs;
|
||||
Map<Event, List<MainPromise*>> waitingMainJobs;
|
||||
std::mutex waitingMainLock;
|
||||
|
||||
void tryMainJob();
|
||||
};
|
||||
|
||||
Map<Event, List<Promise*>> waitingJobs;
|
||||
std::mutex waitingLock;
|
||||
};
|
||||
|
||||
template<bool MainJob>
|
||||
inline JobBase<MainJob> JobPromiseBase<MainJob>::get_return_object() noexcept
|
||||
{
|
||||
id = globalCounter++;
|
||||
return JobBase<MainJob>(std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this), id, finishedEvent);
|
||||
return JobBase<MainJob>(this);
|
||||
}
|
||||
|
||||
template<bool MainJob>
|
||||
inline auto JobPromiseBase<MainJob>::initial_suspend() noexcept
|
||||
{
|
||||
struct JobAwaitable
|
||||
/*struct JobAwaitable
|
||||
{
|
||||
constexpr bool await_ready() { return false; }
|
||||
constexpr void await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
|
||||
@@ -228,7 +257,8 @@ inline auto JobPromiseBase<MainJob>::initial_suspend() noexcept
|
||||
uint64 id;
|
||||
Event& event;
|
||||
};
|
||||
return JobAwaitable{id, finishedEvent};
|
||||
return JobAwaitable{id, finishedEvent};*/
|
||||
return std::suspend_always{};
|
||||
}
|
||||
template<bool MainJob>
|
||||
inline auto JobPromiseBase<MainJob>::final_suspend() const noexcept
|
||||
@@ -248,7 +278,7 @@ inline auto JobPromiseBase<MainJob>::final_suspend() const noexcept
|
||||
template<bool MainJob>
|
||||
inline constexpr void Event::await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
|
||||
{
|
||||
getGlobalThreadPool().enqueueWaiting(*this, std::move(JobBase<MainJob>(h, h.promise().id, *this)));
|
||||
h.promise().enqueue(*this);
|
||||
}
|
||||
|
||||
} // namespace Seele
|
||||
|
||||
@@ -93,11 +93,11 @@ MainJob SceneView::render()
|
||||
.then(depthPrepass.endFrame())
|
||||
.then(lightCullingPass.endFrame())
|
||||
.then(basePass.endFrame())
|
||||
.then([=]() -> MainJob
|
||||
.then([&](Event& event) -> MainJob
|
||||
{
|
||||
renderFinishedEvent.raise();
|
||||
event.raise();
|
||||
co_return;
|
||||
});
|
||||
}(renderFinishedEvent));
|
||||
}
|
||||
|
||||
void SceneView::keyCallback(KeyCode code, InputAction action, KeyModifier)
|
||||
|
||||
@@ -43,7 +43,11 @@ MainJob Window::render()
|
||||
windowView->view->resetRender();
|
||||
}
|
||||
gfxHandle->endFrame();
|
||||
render();
|
||||
if(owner->isActive())
|
||||
{
|
||||
render();
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
Gfx::PWindow Window::getGfxHandle()
|
||||
|
||||
+6
-4
@@ -19,7 +19,7 @@ int main()
|
||||
mainWindowInfo.pixelFormat = Gfx::SE_FORMAT_B8G8R8A8_UNORM;
|
||||
auto window = windowManager->addWindow(mainWindowInfo);
|
||||
ViewportCreateInfo sceneViewInfo;
|
||||
sceneViewInfo.sizeX = 640;
|
||||
sceneViewInfo.sizeX = 1280;
|
||||
sceneViewInfo.sizeY = 720;
|
||||
sceneViewInfo.offsetX = 0;
|
||||
sceneViewInfo.offsetY = 0;
|
||||
@@ -31,10 +31,12 @@ int main()
|
||||
inspectorViewInfo.sizeY = 720;
|
||||
inspectorViewInfo.offsetX = 640;
|
||||
inspectorViewInfo.offsetY = 0;
|
||||
PInspectorView inspectorView = new InspectorView(windowManager->getGraphics(), window, inspectorViewInfo);
|
||||
window->addView(inspectorView);
|
||||
//PInspectorView inspectorView = new InspectorView(windowManager->getGraphics(), window, inspectorViewInfo);
|
||||
//window->addView(inspectorView);
|
||||
sceneView->setFocused();
|
||||
window->render();
|
||||
{
|
||||
window->render();
|
||||
}
|
||||
getGlobalThreadPool().threadLoop(true);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user