Works, but with memory leaks

This commit is contained in:
Dynamitos
2022-03-26 12:55:04 +01:00
parent cd28e433cc
commit 9130a7961f
23 changed files with 720 additions and 308 deletions
+5 -5
View File
@@ -12,7 +12,7 @@
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/bin/Debug",
"console": "internalConsole",
"console": "integratedTerminal",
"environment": [],
"externalConsole": false,
"setupCommands": [
@@ -31,7 +31,7 @@
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/bin/Debug",
"console": "internalConsole",
"console": "integratedTerminal",
"environment": [],
"symbolSearchPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.25.28610\\lib\\x64",
"requireExactSource": false,
@@ -51,7 +51,7 @@
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/bin/Release",
"console": "internalConsole",
"console": "integratedTerminal",
"environment": [],
"symbolSearchPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.25.28610\\lib\\x64",
"requireExactSource": false,
@@ -70,7 +70,7 @@
"program": "${workspaceRoot}/bin/Debug/Seele_unit_tests",
"args": [],
"stopAtEntry": false,
"console": "internalConsole",
"console": "integratedTerminal",
"cwd": "${workspaceRoot}/bin/Debug",
"environment": [],
},
@@ -81,7 +81,7 @@
"program": "${workspaceRoot}/bin/Debug/Seele_unit_tests.exe",
"args": ["--detect_memory_leaks=15533"],
"stopAtEntry": false,
"console": "internalConsole",
"console": "integratedTerminal",
"cwd": "${workspaceRoot}/bin/Debug",
"visualizerFile": "${workspaceRoot}/Seele.natvis",
"environment": [],
+1 -1
View File
@@ -111,7 +111,7 @@ add_subdirectory(src/)
if(MSVC)
set(_CRT_SECURE_NO_WARNINGS)
target_compile_options(Engine PRIVATE /Zi /MP)
target_compile_options(Engine PRIVATE /Zi /MP /DEBUG:FASTLINK)
else()
target_compile_options(Engine PRIVATE -g -Wall -Wextra -Wno-delete-incomplete -pedantic -fcoroutines)
endif()
-7
View File
@@ -33,13 +33,6 @@
"settings": {
"CMAKE_PLATFORM": "x64"
}
},
"x86": {
"short": "x86",
"long": "x86 platform",
"settings": {
"CMAKE_PLATFORM": "x86"
}
}
}
},
+414
View File
@@ -0,0 +1,414 @@
#pragma once
#include "MinimalEngine.h"
#include "Array.h"
namespace Seele
{
template<typename T, Allocator = std::pmr::polymorphic_allocator<T>>
class RingBuffer
{
public:
template <typename X>
class IteratorBase
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = X;
using difference_type = std::ptrdiff_t;
using reference = X&;
using pointer = X*;
IteratorBase(Array<T>& arr, size_t index)
: arr(arr)
, index(index)
{
}
reference operator*() const
{
return arr[index];
}
pointer operator->() const
{
return &arr[index];
}
inline bool operator!=(const IteratorBase &other) const
{
return arr != other.arr && index != other.index;
}
inline bool operator==(const IteratorBase &other) const
{
return arr == other.arr && index == other.index;
}
inline int operator-(const IteratorBase &other) const
{
return (int)(index - other.index);
}
IteratorBase &operator++()
{
index = (index + 1) % arr.size();
return *this;
}
IteratorBase &operator--()
{
index = (index + 1) % arr.size();
return *this;
}
IteratorBase operator++(int)
{
IteratorBase tmp(*this);
++*this;
return tmp;
}
IteratorBase operator--(int)
{
IteratorBase tmp(*this);
--*this;
return tmp;
}
private:
Array<T, Allocator>& arr;
size_t index;
};
using value_type = T;
using allocator_type = Allocator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = value_type&;
using const_reference = const value_type&;
using Iterator = IteratorBase<T>;
using ConstIterator = IteratorBase<const T>;
using iterator = Iterator;
using const_iterator = ConstIterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr RingBuffer() noexcept(noexcept(Allocator()))
{
refreshIterators();
}
constexpr explicit RingBuffer(const allocator_type& alloc)
: data(alloc)
{
refreshIterators();
}
constexpr RingBuffer(size_type size, const value_type& value, const allocator_type& alloc = allocator_type())
: data(size, value, alloc)
, end(size)
{
refreshIterators();
}
constexpr explicit RingBuffer(size_type size, const allocator_type& alloc = allocator_type())
: arraySize(size)
, allocated(size)
, allocator(alloc)
{
_data = allocateRingBuffer(size);
assert(_data != nullptr);
for (size_type i = 0; i < size; ++i)
{
std::allocator_traits<allocator_type>::construct(allocator, &_data[i]);
}
markIteratorDirty();
}
constexpr RingBuffer(std::initializer_list<T> init, const allocator_type& alloc = allocator_type())
: arraySize(init.size())
, allocated(init.size())
, allocator(alloc)
{
_data = allocateRingBuffer(init.size());
assert(_data != nullptr);
markIteratorDirty();
std::uninitialized_copy(init.begin(), init.end(), begin());
}
RingBuffer(const RingBuffer &other)
: arraySize(other.arraySize)
, allocated(other.allocated)
, allocator(std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.allocator))
{
_data = allocateRingBuffer(other.allocated);
assert(_data != nullptr);
markIteratorDirty();
std::uninitialized_copy(other.begin(), other.end(), begin());
}
RingBuffer(RingBuffer &&other) noexcept
: arraySize(std::move(other.arraySize))
, allocated(std::move(other.allocated))
, allocator(std::move(other.allocator))
{
_data = other._data;
other._data = nullptr;
other.allocated = 0;
other.arraySize = 0;
markIteratorDirty();
}
RingBuffer &operator=(const RingBuffer &other) noexcept
{
if (this != &other)
{
if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value )
{
if (!std::allocator_traits<allocator_type>::is_always_equal::value
&& allocator != other.allocator)
{
clear();
}
allocator = other.allocator;
}
if (other.arraySize > allocated)
{
clear();
}
if(_data == nullptr)
{
_data = allocateRingBuffer(other.allocated);
allocated = other.allocated;
}
arraySize = other.arraySize;
markIteratorDirty();
std::uninitialized_copy(other.begin(), other.end(), begin());
}
return *this;
}
RingBuffer &operator=(RingBuffer &&other) noexcept
{
if (this != &other)
{
if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value)
{
allocator = std::move(other.allocator);
}
if (_data != nullptr)
{
clear();
}
allocated = std::move(other.allocated);
arraySize = std::move(other.arraySize);
_data = other._data;
other._data = nullptr;
markIteratorDirty();
}
return *this;
}
~RingBuffer()
{
clear();
}
constexpr bool operator==(const RingBuffer &other)
{
return _data == other._data;
}
constexpr bool operator!=(const RingBuffer &other)
{
return !(*this == other);
}
constexpr iterator find(const value_type &item)
{
for (uint32 i = 0; i < arraySize; ++i)
{
if (_data[i] == item)
{
return iterator(&_data[i]);
}
}
return endIt;
}
constexpr iterator find(value_type&& item)
{
for (uint32 i = 0; i < arraySize; ++i)
{
if (_data[i] == item)
{
return iterator(&_data[i]);
}
}
return endIt;
}
constexpr allocator_type get_allocator() const
{
return allocator;
}
constexpr iterator begin() const
{
return beginIt;
}
constexpr iterator end() const
{
return endIt;
}
constexpr const_iterator cbegin() const
{
return beginIt;
}
constexpr const_iterator cend() const
{
return endIt;
}
constexpr reference add(const value_type &item = value_type())
{
return addInternal(item);
}
constexpr reference add(value_type&& item)
{
return addInternal(std::forward<T>(item));
}
constexpr void addAll(RingBuffer other)
{
for(auto value : other)
{
addInternal(value);
}
}
constexpr reference addUnique(const value_type &item = value_type())
{
iterator it;
if((it = std::move(find(item))) != endIt)
{
return *it;
}
return addInternal(item);
}
template<typename... args>
constexpr reference emplace(args... arguments)
{
if (arraySize == allocated)
{
size_type newSize = arraySize + 1;
allocated = calculateGrowth(newSize);
T *tempRingBuffer = allocateRingBuffer(allocated);
assert(tempRingBuffer != nullptr);
std::uninitialized_move(begin(), end(), Iterator(tempRingBuffer));
deallocateRingBuffer(_data, arraySize);
_data = tempRingBuffer;
}
std::allocator_traits<allocator_type>::construct(allocator, &_data[arraySize++], arguments...);
markIteratorDirty();
return _data[arraySize - 1];
}
constexpr void remove(iterator it, bool keepOrder = true)
{
remove(it - beginIt, keepOrder);
}
constexpr void remove(size_type index, bool keepOrder = true)
{
if (keepOrder)
{
for(uint32 i = index; i < arraySize-1; ++i)
{
_data[i] = std::move(_data[i+1]);
}
}
else
{
_data[index] = std::move(_data[arraySize - 1]);
}
std::allocator_traits<allocator_type>::destroy(allocator, &_data[--arraySize]);
markIteratorDirty();
}
constexpr void resize(size_type newSize)
{
resizeInternal(newSize, std::move(T()));
}
constexpr void resize(size_type newSize, const value_type& value)
{
resizeInternal(newSize, value);
}
constexpr void clear()
{
if(_data == nullptr)
{
return;
}
for(size_type i = 0; i < arraySize; ++i)
{
std::allocator_traits<allocator_type>::destroy(allocator, &_data[i]);
}
deallocateRingBuffer(_data, allocated);
_data = nullptr;
arraySize = 0;
allocated = 0;
markIteratorDirty();
}
inline size_type indexOf(iterator iterator)
{
return iterator - beginIt;
}
inline size_type indexOf(const_iterator iterator) const
{
return iterator.p - beginIt.p;
}
inline size_type indexOf(T& t)
{
return indexOf(find(t));
}
inline size_type indexOf(const T& t) const
{
return indexOf(find(t));
}
inline size_type size() const
{
return arraySize;
}
inline size_type empty() const
{
return arraySize == 0;
}
inline size_type capacity() const
{
return allocated;
}
inline pointer data() const
{
return _data;
}
inline reference front() const
{
assert(arraySize > 0);
return _data[0];
}
inline reference back() const
{
assert(arraySize > 0);
return _data[arraySize - 1];
}
void pop()
{
std::allocator_traits<allocator_type>::destroy(allocator, &_data[--arraySize]);
markIteratorDirty();
}
constexpr inline reference operator[](size_type index)
{
assert(index < arraySize);
return _data[index];
}
constexpr inline const reference operator[](size_type index) const
{
assert(index < arraySize);
return _data[index];
}
private:
void refreshIterators()
{
beginIt = Iterator(data, begin);
endIt = Iterator(data, end);
}
size_type begin = 0;
size_type end = 0;
iterator beginIt;
iterator endIt;
Array<T, Allocator> data;
};
class StaticRingBuffer
{
};
} // namespace Seele
@@ -171,12 +171,6 @@ void CmdBuffer::waitForCommand(uint32 timeout)
refreshFence();
}
Event CmdBuffer::asyncWait() const
{
return fence->asyncWait();
}
PFence CmdBuffer::getFence()
{
return fence;
@@ -32,7 +32,10 @@ public:
void addWaitSemaphore(VkPipelineStageFlags stages, PSemaphore waitSemaphore);
void refreshFence();
void waitForCommand(uint32 timeToWait = 1000000u);
Event asyncWait() const;
Fence* operator co_await()
{
return fence.getHandle();
}
PFence getFence();
PCommandBufferManager getManager();
enum State
@@ -80,11 +80,6 @@ void Fence::wait(uint32 timeout)
break;
}
}
Event Fence::asyncWait() const
{
return signaled;
}
VertexDeclaration::VertexDeclaration(const Array<Gfx::VertexElement>& elementList)
: elementList(elementList)
@@ -43,7 +43,10 @@ public:
return fence;
}
void wait(uint32 timeout);
Event asyncWait() const;
Event& operator co_await()
{
return signaled;
}
bool operator<(const Fence &other) const
{
return fence < other.fence;
+3 -3
View File
@@ -146,15 +146,15 @@ Vector Transform::getScale() const
Vector Transform::getForward() const
{
return Vector(0, 0, 1) * rotation;
return glm::normalize(Vector(0, 0, 1) * rotation);
}
Vector Transform::getRight() const
{
return Vector(1, 0, 0) * rotation;
return glm::normalize(Vector(1, 0, 0) * rotation);
}
Vector Transform::getUp() const
{
return Vector(0, 1, 0) * rotation;
return glm::normalize(Vector(0, 1, 0) * rotation);
}
bool Transform::equals(const Transform &other, float tolerance)
+1
View File
@@ -14,6 +14,7 @@ CameraActor::CameraActor()
cameraComponent->aspectRatio = 1.777778f;
cameraComponent->setParent(sceneComponent);
cameraComponent->setOwner(this);
sceneComponent->addChildComponent(cameraComponent);
}
CameraActor::~CameraActor()
+14 -10
View File
@@ -13,8 +13,8 @@ CameraComponent::CameraComponent()
, projectionMatrix(Matrix4())
, viewMatrix(Matrix4())
{
rotationX = 0;
rotationY = 0;
yaw = 0;
pitch = 0;
setRelativeLocation(Vector(0, 10, -50));
}
@@ -22,12 +22,16 @@ CameraComponent::~CameraComponent()
{
}
void CameraComponent::mouseMove(float deltaX, float deltaY)
void CameraComponent::mouseMove(float deltaYaw, float deltaPitch)
{
rotationX -= deltaX / 1000.f;
rotationY += deltaY / 1000.f;
//std::cout << "X:" << rotationX << " Y: " << rotationY << std::endl;
setRelativeRotation(Vector(rotationY, rotationX, 0));
yaw -= deltaYaw / 500.f;
pitch += deltaPitch / 500.f;
//std::cout << "Yaw: " << yaw << " Pitch: " << pitch << std::endl;
Vector cameraDirection = glm::normalize(Vector(cos(yaw) * cos(pitch), sin(pitch), sin(yaw) * cos(pitch)));
Vector xyz = glm::cross(cameraDirection, Vector(0, 0, 1));
Quaternion result = Quaternion(glm::dot(cameraDirection, Vector(0, 0, 1)) + 1, xyz.x, xyz.y, xyz.z);
//std::cout << "Result " << Vector(0, 0, 1) * glm::normalize(result) << " cameraDirection: " << cameraDirection << std::endl;
setRelativeRotation(result);
bNeedsViewBuild = true;
}
@@ -51,9 +55,9 @@ void CameraComponent::moveY(float amount)
void CameraComponent::buildViewMatrix()
{
Vector eyePos = getTransform().getPosition();
Vector lookAt = eyePos + getTransform().getForward();
std::cout << "Eye: " << eyePos << " lookAt: " << lookAt << std::endl;
Vector eyePos = getAbsoluteTransform().getPosition();
Vector lookAt = eyePos + glm::normalize(Vector(cos(yaw) * cos(pitch), sin(pitch), sin(yaw) * cos(pitch)));
//std::cout << "Eye: " << eyePos << " lookAt: " << lookAt << std::endl;
viewMatrix = glm::lookAt(eyePos, lookAt, Vector(0, 1, 0));
bNeedsViewBuild = false;
@@ -45,8 +45,8 @@ private:
//Transforms relative to actor
Matrix4 viewMatrix;
Matrix4 projectionMatrix;
float rotationX;
float rotationY;
float yaw;
float pitch;
};
DEFINE_REF(CameraComponent)
} // namespace Seele
+23 -4
View File
@@ -128,18 +128,22 @@ void Component::notifySceneAttach(PScene scene)
void Component::setRelativeLocation(Vector location)
{
transform = Transform(location, transform.getRotation(), transform.getScale());
propagateTransformUpdate();
}
void Component::setRelativeRotation(Vector rotation)
{
transform = Transform(transform.getPosition(), Quaternion(rotation), transform.getScale());
propagateTransformUpdate();
}
void Component::setRelativeRotation(Quaternion rotation)
{
transform = Transform(transform.getPosition(), rotation, transform.getScale());
propagateTransformUpdate();
}
void Component::setRelativeScale(Vector scale)
{
transform = Transform(transform.getPosition(), transform.getRotation(), scale);
propagateTransformUpdate();
}
//void Component::addAbsoluteTranslation(Vector translation)
@@ -161,14 +165,17 @@ void Component::setRelativeScale(Vector scale)
void Component::addRelativeLocation(Vector translation)
{
transform = Transform(transform.getPosition() + translation, transform.getRotation(), transform.getScale());
propagateTransformUpdate();
}
void Component::addRelativeRotation(Vector rotation)
{
transform = Transform(transform.getPosition(), transform.getRotation() + Quaternion(rotation), transform.getScale());
transform = Transform(transform.getPosition(), transform.getRotation() * Quaternion(rotation), transform.getScale());
propagateTransformUpdate();
}
void Component::addRelativeRotation(Quaternion rotation)
{
transform = Transform(transform.getPosition(), transform.getRotation(), transform.getScale());
transform = Transform(transform.getPosition(), transform.getRotation() * rotation, transform.getScale());
propagateTransformUpdate();
}
Transform Component::getTransform() const
@@ -177,10 +184,22 @@ Transform Component::getTransform() const
}
Transform Component::getAbsoluteTransform() const
{
return absoluteTransform;
}
void Component::propagateTransformUpdate()
{
if(parent != nullptr)
{
return transform + parent->getAbsoluteTransform();
absoluteTransform = transform + parent->getAbsoluteTransform();
}
else
{
absoluteTransform = transform;
}
for(auto child : children)
{
child->propagateTransformUpdate();
}
return transform;
}
+2
View File
@@ -74,7 +74,9 @@ private:
}
return nullptr;
}
void propagateTransformUpdate();
Transform transform;
Transform absoluteTransform;
PScene owningScene;
PActor owner;
PComponent parent;
+73 -92
View File
@@ -3,55 +3,69 @@
using namespace Seele;
Event::Event()
: flag(std::make_shared<StateStore>())
{
}
std::mutex Seele::promisesLock;
List<Promise*> Seele::promises;
Event::Event(nullptr_t)
: flag(nullptr)
{
}
Event::Event(const std::string &name)
: flag(std::make_shared<StateStore>())
Event::Event(const std::string &name, const std::source_location &location)
: name(name)
, location(location)
{
flag->name = name;
}
Event::Event(const std::source_location &location)
: flag(std::make_shared<StateStore>())
: name(location.function_name())
, location(location)
{
flag->name = location.function_name();
flag->location = location;
}
void Event::raise()
{
std::scoped_lock lock(flag->lock);
flag->data = 1;
getGlobalThreadPool().notify(*this);
std::scoped_lock lock(eventLock);
data = true;
if(waitingJobs.size() > 0)
{
getGlobalThreadPool().scheduleBatch(waitingJobs);
}
if(waitingMainJobs.size() > 0)
{
getGlobalThreadPool().scheduleBatch(waitingMainJobs);
}
}
void Event::reset()
{
std::scoped_lock lock(flag->lock);
flag->data = 0;
std::scoped_lock lock(eventLock);
data = false;
}
bool Event::await_ready()
{
flag->lock.lock();
bool result = flag->data;
eventLock.lock();
bool result = data;
if(result)
{
flag->lock.unlock();
eventLock.unlock();
}
return result;
}
void Event::await_suspend(std::coroutine_handle<JobPromiseBase<false>> h)
{
//h.promise().enqueue(this);
waitingJobs.add(JobBase<false>(&h.promise()));
eventLock.unlock();
}
void Event::await_suspend(std::coroutine_handle<JobPromiseBase<true>> h)
{
//h.promise().enqueue(this);
waitingMainJobs.add(JobBase<true>(&h.promise()));
eventLock.unlock();
}
ThreadPool::ThreadPool(uint32 threadCount)
: workers(threadCount)
{
@@ -59,13 +73,24 @@ ThreadPool::ThreadPool(uint32 threadCount)
for (uint32 i = 0; i < threadCount; ++i)
{
workers[i] = std::thread(&ThreadPool::threadLoop, this);
workers[i].detach();
}
}
ThreadPool::~ThreadPool()
{
workers.clear();
running.store(false);
{
std::unique_lock lock(mainJobLock);
mainJobCV.notify_all();
}
{
std::unique_lock lock(jobQueueLock);
jobQueueCV.notify_all();
}
for(auto& worker : workers)
{
worker.join();
}
}
void ThreadPool::waitIdle()
@@ -73,108 +98,63 @@ void ThreadPool::waitIdle()
while(true)
{
std::unique_lock lock(numIdlingLock);
numIdlingIncr.wait(lock);
if(numIdling == workers.size())
{
return;
}
numIdlingIncr.wait(lock);
}
}
void ThreadPool::enqueueWaiting(Event &event, Promise* job)
void ThreadPool::scheduleJob(Job job)
{
assert(!job->done());
std::scoped_lock lock(waitingLock);
//std::cout << "Job " << job->finishedEvent.name << " waiting on event " << event.name << std::endl;
waitingJobs[event].add(job);
job->addRef();
}
void ThreadPool::enqueueWaiting(Event &event, MainPromise* job)
{
assert(!job->done());
std::scoped_lock lock(waitingMainLock);
//std::cout << job->finishedEvent.name << " waiting on event " << event.name << std::endl;
waitingMainJobs[event].add(job);
job->addRef();
}
void ThreadPool::scheduleJob(Promise* job)
{
assert(!job->done());
assert(!job.done());
std::scoped_lock lock(jobQueueLock);
//std::cout << "Queueing job " << job->finishedEvent << std::endl;
jobQueue.add(job);
jobQueue.add(std::move(job));
jobQueueCV.notify_one();
job->addRef();
}
void ThreadPool::scheduleJob(MainPromise* job)
void ThreadPool::scheduleJob(MainJob job)
{
assert(!job->done());
assert(!job.done());
std::scoped_lock lock(mainJobLock);
//std::cout << "Queueing job " << job->finishedEvent << std::endl;
mainJobs.add(job);
mainJobs.add(std::move(job));
mainJobCV.notify_one();
job->addRef();
}
void ThreadPool::notify(Event &event)
{
//std::cout << "Event " << event.name << " raised" << std::endl;
{
std::scoped_lock lock(jobQueueLock, waitingLock);
List<Promise*> jobs = std::move(waitingJobs[event]);
waitingJobs.erase(event);
for (auto &job : jobs)
{
//assert(job.id != -1ull);
//std::cout << "Waking up " << job->finishedEvent.name << std::endl;
job->state = Promise::State::SCHEDULED;
jobQueue.add(job);
jobQueueCV.notify_one();
}
}
{
std::scoped_lock lock(mainJobLock, waitingMainLock);
List<MainPromise*> jobs = std::move(waitingMainJobs[event]);
waitingMainJobs.erase(event);
for (auto &job : jobs)
{
//assert(job.id != -1ull);
//std::cout << "Waking up main " << job->finishedEvent.name << std::endl;
job->state = MainPromise::State::SCHEDULED;
mainJobs.add(job);
mainJobCV.notify_one();
}
}
}
void ThreadPool::mainLoop()
{
while(true)
while(running.load())
{
MainPromise* job;
MainJob job;
{
std::unique_lock lock(mainJobLock);
if(mainJobs.empty())
{
mainJobCV.wait(lock);
}
[[likely]]
if(!mainJobs.empty())
{
job = mainJobs.front();
mainJobs.popFront();
}
job->resume();
job->removeRef();
else
{
continue;
}
}
job.resume();
}
}
void ThreadPool::threadLoop()
{
List<Promise*> localQueue;
while (true)
List<Job> localQueue;
while (running.load())
{
[[likely]]
if(!localQueue.empty())
{
Promise* job = localQueue.retrieve();
job->resume();
job->removeRef();
Job job = localQueue.retrieve();
job.resume();
}
else
{
@@ -194,7 +174,8 @@ void ThreadPool::threadLoop()
}
// take 1/numThreads jobs, maybe make this a parameter that
// adjusts based on past workload
uint32 numTaken = std::max(jobQueue.size() / workers.size(), 1ull);
uint32 partitionedWorkload = (uint32)(jobQueue.size() / workers.size());
uint32 numTaken = std::clamp(partitionedWorkload, 1u, localQueueSize);
while (!jobQueue.empty() && localQueue.size() < numTaken)
{
localQueue.add(jobQueue.retrieve());
+77 -88
View File
@@ -10,66 +10,65 @@ namespace Seele
{
extern class ThreadPool& getGlobalThreadPool();
template<bool MainJob>
struct JobBase;
template<bool MainJob>
struct JobPromiseBase;
struct Event
{
public:
Event();
Event(nullptr_t);
Event(const std::string& name);
Event(const std::source_location& location);
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) = delete;
Event(Event&& other) = default;
~Event() = default;
Event& operator=(const Event& other) = delete;
Event& operator=(Event&& other) = default;
auto operator<=>(const Event& other) const
{
return flag <=> other.flag;
return name <=> other.name;
}
bool operator==(const Event& other) const
{
return flag == other.flag;
}
Event operator co_await()
{
return *this;
return name == other.name;
}
operator bool()
{
std::scoped_lock lock(flag->lock);
return flag->data;
std::scoped_lock lock(eventLock);
return data;
}
friend std::ostream& operator<<(std::ostream& stream, const Event& event)
{
stream
<< event.flag->location.file_name()
<< event.location.file_name()
<< "("
<< event.flag->location.line()
<< event.location.line()
<< ":"
<< event.flag->location.column()
<< event.location.column()
<< "): "
<< event.flag->location.function_name();
<< event.location.function_name();
return stream;
}
void raise();
void reset();
bool await_ready();
template<bool MainJob>
constexpr void await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h);
void await_suspend(std::coroutine_handle<JobPromiseBase<false>> h);
void await_suspend(std::coroutine_handle<JobPromiseBase<true>> h);
constexpr void await_resume() {}
private:
struct StateStore
{
std::mutex lock;
std::mutex eventLock;
std::string name;
std::source_location location;
bool data;
};
std::shared_ptr<StateStore> flag;
bool data = false;
Array<JobBase<false>> waitingJobs;
Array<JobBase<true>> waitingMainJobs;
friend class ThreadPool;
};
template<bool MainJob>
struct JobBase;
extern std::mutex promisesLock;
extern List<JobPromiseBase<false>*> promises;
template<bool MainJob>
struct JobPromiseBase
{
@@ -82,12 +81,24 @@ struct JobPromiseBase
DONE
};
JobPromiseBase(const std::source_location& location = std::source_location::current())
: handle(std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this))
, finishedEvent(Event(location))
{
handle = std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this);
finishedEvent = Event(location);
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;
@@ -102,7 +113,6 @@ struct JobPromiseBase
void resume()
{
std::scoped_lock lock(promiseLock);
if(!handle || handle.done() || executing())
{
return;
@@ -116,17 +126,16 @@ struct JobPromiseBase
finishedEvent.raise();
if(continuation)
{
std::scoped_lock lock(continuation->promiseLock);
getGlobalThreadPool().scheduleJob(continuation);
getGlobalThreadPool().scheduleJob(JobBase<MainJob>(continuation));
continuation->removeRef();
}
}
void setContinuation(JobPromiseBase* cont)
{
std::scoped_lock lock(promiseLock, cont->promiseLock);
assert(cont->ready());
continuation = cont;
cont->state = State::SCHEDULED;
cont->waitingFor = &finishedEvent;
cont->addRef();
}
bool done()
@@ -149,29 +158,25 @@ struct JobPromiseBase
{
return state == State::READY;
}
void reset()
{
std::scoped_lock lock(promiseLock);
finishedEvent.reset();
}
void enqueue(Event& event)
void enqueue(Event* event)
{
if(!handle || handle.done() || waiting() || scheduled())
{
return;
}
state = State::WAITING;
getGlobalThreadPool().enqueueWaiting(event, this);
waitingFor = event;
getGlobalThreadPool().enqueueWaiting(event, std::move(JobBase<MainJob>(this)));
}
void schedule()
bool schedule()
{
std::scoped_lock lock(promiseLock);
if(!handle || done() || !ready())
{
return;
return false;
}
state = State::SCHEDULED;
getGlobalThreadPool().scheduleJob(this);
getGlobalThreadPool().scheduleJob(std::move(JobBase<MainJob>(this)));
return true;
}
void addRef()
{
@@ -179,25 +184,23 @@ struct JobPromiseBase
}
void removeRef()
{
if(--numRefs < 1)
numRefs--;
if(numRefs == 0)
{
if(done())
if(!schedule())
{
handle.destroy();
}
else
{
schedule();
}
}
}
std::mutex promiseLock;
uint64 pad0 = 0x7472617453;
std::coroutine_handle<JobPromiseBase> handle;
Event* waitingFor = nullptr;
JobPromiseBase* continuation = nullptr;
std::atomic_uint64_t numRefs = 0;
uint64 numRefs = 0;
Event finishedEvent;
State state = State::READY;
uint64 pad1 = 0x646E45;
};
template<bool MainJob = false>
@@ -213,16 +216,15 @@ public:
explicit JobBase(JobPromiseBase<MainJob>* promise)
: promise(promise)
{
promise->addRef();
}
JobBase(const JobBase& other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
promise->addRef();
}
JobBase(JobBase&& other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
other.promise = nullptr;
}
@@ -238,7 +240,10 @@ public:
{
if(this != &other)
{
std::scoped_lock lock(other.promise->promiseLock);
if(promise != nullptr)
{
promise->removeRef();
}
promise = other.promise;
promise->addRef();
}
@@ -248,7 +253,10 @@ public:
{
if(this != &other)
{
std::scoped_lock lock(other.promise->promiseLock);
if(promise != nullptr)
{
promise->removeRef();
}
promise = other.promise;
other.promise = nullptr;
}
@@ -273,7 +281,7 @@ public:
{
return promise->done();
}
Event operator co_await() const
Event& operator co_await()
{
// the co_await operator keeps a reference to this, it won't
// be scheduled from the destructor
@@ -311,8 +319,7 @@ public:
Array<JobBase> jobs;
for(auto&& param : params)
{
JobBase base = func(param);
jobs.add(base);
jobs.add(func(param));
}
getGlobalThreadPool().scheduleBatch(jobs);
for(auto job : jobs)
@@ -333,15 +340,11 @@ using Promise = JobPromiseBase<false>;
class ThreadPool
{
public:
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
ThreadPool(uint32 threadCount = 1);//std::thread::hardware_concurrency());
virtual ~ThreadPool();
void waitIdle();
// Adds a job to the waiting queue for event
void enqueueWaiting(Event& event, Promise* job);
// Adds a job to the waiting queue for event
void enqueueWaiting(Event& event, MainPromise* job);
void scheduleJob(Promise* job);
void scheduleJob(MainPromise* job);
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)
@@ -349,9 +352,9 @@ public:
std::scoped_lock lock(mainJobLock);
for(auto job : jobs)
{
job.promise->addRef();
//job.promise->addRef();
job.promise->state = JobPromiseBase<true>::State::SCHEDULED;
mainJobs.add(job.promise);
mainJobs.add(job);
}
mainJobCV.notify_one();
}
@@ -362,13 +365,13 @@ public:
std::scoped_lock lock(jobQueueLock);
for(auto job : jobs)
{
job.promise->addRef();
//job.promise->addRef();
job.promise->state = JobPromiseBase<false>::State::SCHEDULED;
jobQueue.add(job.promise);
jobQueue.add(job);
}
jobQueueCV.notify_all();
}
void notify(Event& event);
void notify(Event* event);
void mainLoop();
void threadLoop();
private:
@@ -378,27 +381,20 @@ private:
uint32 numIdling;
Array<std::thread> workers;
List<MainPromise*> mainJobs;
List<MainJob> mainJobs;
std::mutex mainJobLock;
std::condition_variable mainJobCV;
List<Promise*> jobQueue;
List<Job> jobQueue;
std::mutex jobQueueLock;
std::condition_variable jobQueueCV;
Map<Event, List<MainPromise*>> waitingMainJobs;
std::mutex waitingMainLock;
Map<Event, List<Promise*>> waitingJobs;
std::mutex waitingLock;
uint32 maxLocalQueueSize = 50;
uint32 localQueueSize = 50;
};
template<bool MainJob>
inline JobBase<MainJob> JobPromiseBase<MainJob>::get_return_object() noexcept
{
numRefs++;
return JobBase<MainJob>(this);
}
@@ -415,11 +411,4 @@ inline auto JobPromiseBase<MainJob>::final_suspend() noexcept
return std::suspend_always{};
}
template<bool MainJob>
inline constexpr void Event::await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
{
h.promise().enqueue(*this);
flag->lock.unlock();
}
} // namespace Seele
+5
View File
@@ -25,3 +25,8 @@ void View::setFocused()
{
owner->setFocused(this);
}
const std::string& View::getName()
{
return name;
}
+2
View File
@@ -25,6 +25,8 @@ public:
void applyArea(URect area);
void setFocused();
const std::string& getName();
protected:
Gfx::PGraphics graphics;
Gfx::PViewport viewport;
+1 -3
View File
@@ -16,9 +16,7 @@ Window::~Window()
void Window::addView(PView view)
{
WindowView* windowView = new WindowView();
windowView->view = view;
windowView->updateFinished = Event(view->name);
WindowView* windowView = new WindowView(view);
//windowView->worker = std::thread(&Window::viewWorker, this, windowView);
views.add(windowView);
viewWorker(views.size() - 1);
+4
View File
@@ -10,6 +10,10 @@ struct WindowView
PView view;
Event updateFinished;
std::mutex workerMutex;
WindowView(PView view)
: view(view)
, updateFinished(view->getName())
{}
};
DEFINE_REF(WindowView)
DECLARE_REF(WindowManager)
+8
View File
@@ -1,9 +1,17 @@
#include "EngineTest.h"
#include "MinimalEngine.h"
#include "ThreadPool.h"
#define BOOST_TEST_MODULE SeeleEngine
#include <boost/test/unit_test.hpp>
//#include <vld.h>
using namespace Seele;
Seele::GlobalFixture::~GlobalFixture()
{
getGlobalThreadPool().waitIdle();
}
BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
BOOST_AUTO_TEST_SUITE(RefPtr)
+1 -5
View File
@@ -1,6 +1,5 @@
#pragma once
#include <vld.h>
#include "ThreadPool.h"
namespace Seele
{
@@ -10,10 +9,7 @@ namespace Seele
{
//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
}
~GlobalFixture()
{
getGlobalThreadPool().waitIdle();
}
~GlobalFixture();
void setup()
{
//Fibers::JobQueue::initJobQueues();
+2 -1
View File
@@ -42,6 +42,7 @@ Job basicAwaitBase()
basicAwaitState = 25;
co_await basicAwaitThird();
BOOST_REQUIRE_EQUAL(basicAwaitState, 30);
co_return;
}
BOOST_AUTO_TEST_CASE(basic_coawait)
@@ -211,7 +212,7 @@ Job launchStressTest()
BOOST_AUTO_TEST_CASE(stress_test)
{
//launchStressTest();
launchStressTest();
}
BOOST_AUTO_TEST_SUITE_END()