diff --git a/.vscode/launch.json b/.vscode/launch.json index dc76048..017e737 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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": [], diff --git a/CMakeLists.txt b/CMakeLists.txt index 881c1a2..5c683d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/cmake-variants.json b/cmake-variants.json index 195bd88..8f2f00f 100644 --- a/cmake-variants.json +++ b/cmake-variants.json @@ -33,13 +33,6 @@ "settings": { "CMAKE_PLATFORM": "x64" } - }, - "x86": { - "short": "x86", - "long": "x86 platform", - "settings": { - "CMAKE_PLATFORM": "x86" - } } } }, diff --git a/src/Engine/Containers/RingBuffer.h b/src/Engine/Containers/RingBuffer.h new file mode 100644 index 0000000..f75d0aa --- /dev/null +++ b/src/Engine/Containers/RingBuffer.h @@ -0,0 +1,414 @@ +#pragma once +#include "MinimalEngine.h" +#include "Array.h" + +namespace Seele +{ +template> +class RingBuffer +{ +public: + template + 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& 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& 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; + using ConstIterator = IteratorBase; + using iterator = Iterator; + using const_iterator = ConstIterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_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::construct(allocator, &_data[i]); + } + markIteratorDirty(); + } + constexpr RingBuffer(std::initializer_list 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::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::propagate_on_container_copy_assignment::value ) + { + if (!std::allocator_traits::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::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(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 + 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::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::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::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::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 data; +}; +class StaticRingBuffer +{ + +}; +} // namespace Seele diff --git a/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.cpp b/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.cpp index 6517c73..ac3a30e 100644 --- a/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.cpp +++ b/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.cpp @@ -171,12 +171,6 @@ void CmdBuffer::waitForCommand(uint32 timeout) refreshFence(); } -Event CmdBuffer::asyncWait() const -{ - return fence->asyncWait(); -} - - PFence CmdBuffer::getFence() { return fence; diff --git a/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.h b/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.h index 59aee44..c2e6c63 100644 --- a/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.h +++ b/src/Engine/Graphics/Vulkan/VulkanCommandBuffer.h @@ -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 diff --git a/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.cpp b/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.cpp index 192ac15..fc842f8 100644 --- a/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.cpp +++ b/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.cpp @@ -80,11 +80,6 @@ void Fence::wait(uint32 timeout) break; } } -Event Fence::asyncWait() const -{ - return signaled; -} - VertexDeclaration::VertexDeclaration(const Array& elementList) : elementList(elementList) diff --git a/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.h b/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.h index 89d514b..cba2e4e 100644 --- a/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.h +++ b/src/Engine/Graphics/Vulkan/VulkanGraphicsResources.h @@ -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; diff --git a/src/Engine/Math/Transform.cpp b/src/Engine/Math/Transform.cpp index 41588a9..1c8ea40 100644 --- a/src/Engine/Math/Transform.cpp +++ b/src/Engine/Math/Transform.cpp @@ -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) diff --git a/src/Engine/Scene/Actor/CameraActor.cpp b/src/Engine/Scene/Actor/CameraActor.cpp index d374a2e..33a9c5a 100644 --- a/src/Engine/Scene/Actor/CameraActor.cpp +++ b/src/Engine/Scene/Actor/CameraActor.cpp @@ -14,6 +14,7 @@ CameraActor::CameraActor() cameraComponent->aspectRatio = 1.777778f; cameraComponent->setParent(sceneComponent); cameraComponent->setOwner(this); + sceneComponent->addChildComponent(cameraComponent); } CameraActor::~CameraActor() diff --git a/src/Engine/Scene/Components/CameraComponent.cpp b/src/Engine/Scene/Components/CameraComponent.cpp index 461fe22..17a4669 100644 --- a/src/Engine/Scene/Components/CameraComponent.cpp +++ b/src/Engine/Scene/Components/CameraComponent.cpp @@ -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; diff --git a/src/Engine/Scene/Components/CameraComponent.h b/src/Engine/Scene/Components/CameraComponent.h index a062448..f6488ec 100644 --- a/src/Engine/Scene/Components/CameraComponent.h +++ b/src/Engine/Scene/Components/CameraComponent.h @@ -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 diff --git a/src/Engine/Scene/Components/Component.cpp b/src/Engine/Scene/Components/Component.cpp index 701ff48..7d77bc4 100644 --- a/src/Engine/Scene/Components/Component.cpp +++ b/src/Engine/Scene/Components/Component.cpp @@ -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; } \ No newline at end of file diff --git a/src/Engine/Scene/Components/Component.h b/src/Engine/Scene/Components/Component.h index 6479cc6..e157548 100644 --- a/src/Engine/Scene/Components/Component.h +++ b/src/Engine/Scene/Components/Component.h @@ -74,7 +74,9 @@ private: } return nullptr; } + void propagateTransformUpdate(); Transform transform; + Transform absoluteTransform; PScene owningScene; PActor owner; PComponent parent; diff --git a/src/Engine/ThreadPool.cpp b/src/Engine/ThreadPool.cpp index 7c2f780..cb2a774 100644 --- a/src/Engine/ThreadPool.cpp +++ b/src/Engine/ThreadPool.cpp @@ -3,55 +3,69 @@ using namespace Seele; - -Event::Event() - : flag(std::make_shared()) -{ -} +std::mutex Seele::promisesLock; +List Seele::promises; Event::Event(nullptr_t) - : flag(nullptr) { } -Event::Event(const std::string &name) - : flag(std::make_shared()) +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()) + : 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> h) +{ + //h.promise().enqueue(this); + waitingJobs.add(JobBase(&h.promise())); + eventLock.unlock(); +} + +void Event::await_suspend(std::coroutine_handle> h) +{ + //h.promise().enqueue(this); + waitingMainJobs.add(JobBase(&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 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 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); } - job = mainJobs.front(); - mainJobs.popFront(); + [[likely]] + if(!mainJobs.empty()) + { + job = mainJobs.front(); + mainJobs.popFront(); + } + else + { + continue; + } } - job->resume(); - job->removeRef(); + job.resume(); } } void ThreadPool::threadLoop() { - List localQueue; - while (true) + List 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()); diff --git a/src/Engine/ThreadPool.h b/src/Engine/ThreadPool.h index 93a53dc..997e8f7 100644 --- a/src/Engine/ThreadPool.h +++ b/src/Engine/ThreadPool.h @@ -9,67 +9,66 @@ namespace Seele { extern class ThreadPool& getGlobalThreadPool(); +template +struct JobBase; template 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 - constexpr void await_suspend(std::coroutine_handle> h); + void await_suspend(std::coroutine_handle> h); + void await_suspend(std::coroutine_handle> h); constexpr void await_resume() {} private: - struct StateStore - { - std::mutex lock; - std::string name; - std::source_location location; - bool data; - }; - std::shared_ptr flag; + std::mutex eventLock; + std::string name; + std::source_location location; + bool data = false; + Array> waitingJobs; + Array> waitingMainJobs; friend class ThreadPool; }; -template -struct JobBase; +extern std::mutex promisesLock; +extern List*> promises; template struct JobPromiseBase { @@ -82,12 +81,24 @@ struct JobPromiseBase DONE }; JobPromiseBase(const std::source_location& location = std::source_location::current()) + : handle(std::coroutine_handle>::from_promise(*this)) + , finishedEvent(Event(location)) { - handle = std::coroutine_handle>::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 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(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(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(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 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 @@ -213,16 +216,15 @@ public: explicit JobBase(JobPromiseBase* 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 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; 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 requires std::same_as, 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::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::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 workers; - List mainJobs; + List mainJobs; std::mutex mainJobLock; std::condition_variable mainJobCV; - List jobQueue; + List jobQueue; std::mutex jobQueueLock; std::condition_variable jobQueueCV; - - Map> waitingMainJobs; - std::mutex waitingMainLock; - Map> waitingJobs; - std::mutex waitingLock; - - uint32 maxLocalQueueSize = 50; + uint32 localQueueSize = 50; }; template inline JobBase JobPromiseBase::get_return_object() noexcept { - numRefs++; return JobBase(this); } @@ -415,11 +411,4 @@ inline auto JobPromiseBase::final_suspend() noexcept return std::suspend_always{}; } -template -inline constexpr void Event::await_suspend(std::coroutine_handle> h) -{ - h.promise().enqueue(*this); - flag->lock.unlock(); -} - } // namespace Seele diff --git a/src/Engine/Window/View.cpp b/src/Engine/Window/View.cpp index e2c9b81..eea5c6b 100644 --- a/src/Engine/Window/View.cpp +++ b/src/Engine/Window/View.cpp @@ -25,3 +25,8 @@ void View::setFocused() { owner->setFocused(this); } + +const std::string& View::getName() +{ + return name; +} diff --git a/src/Engine/Window/View.h b/src/Engine/Window/View.h index 948f908..eee9a2c 100644 --- a/src/Engine/Window/View.h +++ b/src/Engine/Window/View.h @@ -25,6 +25,8 @@ public: void applyArea(URect area); void setFocused(); + const std::string& getName(); + protected: Gfx::PGraphics graphics; Gfx::PViewport viewport; diff --git a/src/Engine/Window/Window.cpp b/src/Engine/Window/Window.cpp index 4f54a68..8a4d66b 100644 --- a/src/Engine/Window/Window.cpp +++ b/src/Engine/Window/Window.cpp @@ -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); diff --git a/src/Engine/Window/Window.h b/src/Engine/Window/Window.h index d6870ed..c16207e 100644 --- a/src/Engine/Window/Window.h +++ b/src/Engine/Window/Window.h @@ -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) diff --git a/test/Engine/EngineTest.cpp b/test/Engine/EngineTest.cpp index 066d7f8..ca6f235 100644 --- a/test/Engine/EngineTest.cpp +++ b/test/Engine/EngineTest.cpp @@ -1,112 +1,120 @@ #include "EngineTest.h" #include "MinimalEngine.h" +#include "ThreadPool.h" #define BOOST_TEST_MODULE SeeleEngine #include +//#include using namespace Seele; + +Seele::GlobalFixture::~GlobalFixture() +{ + getGlobalThreadPool().waitIdle(); +} + BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture); BOOST_AUTO_TEST_SUITE(RefPtr) struct TestStruct { - TestStruct() - : data(10) - { - } - virtual ~TestStruct() - { - data = 15; - } - uint32 data; + TestStruct() + : data(10) + { + } + virtual ~TestStruct() + { + data = 15; + } + uint32 data; }; struct DeclStruct; BOOST_AUTO_TEST_CASE(basic_refcount) { - { - Seele::RefPtr ptr = new TestStruct(); - BOOST_REQUIRE_EQUAL(ptr->data, 10); - { - Seele::RefPtr secondPtr = ptr; - BOOST_REQUIRE_EQUAL(secondPtr->data, 10); - BOOST_REQUIRE_EQUAL(ptr->data, 10); - } - BOOST_REQUIRE_EQUAL(ptr->data, 10); - } + { + Seele::RefPtr ptr = new TestStruct(); + BOOST_REQUIRE_EQUAL(ptr->data, 10); + { + Seele::RefPtr secondPtr = ptr; + BOOST_REQUIRE_EQUAL(secondPtr->data, 10); + BOOST_REQUIRE_EQUAL(ptr->data, 10); + } + BOOST_REQUIRE_EQUAL(ptr->data, 10); + } } struct DeclStruct { - ~DeclStruct() - { - data = 10; - } - uint32 data = 20; + ~DeclStruct() + { + data = 10; + } + uint32 data = 20; }; struct DerivedStruct : public TestStruct { - DerivedStruct() - :data2(20) - { + DerivedStruct() + :data2(20) + { - } - ~DerivedStruct() - { - data2 = 30; - } - uint32 data2; + } + ~DerivedStruct() + { + data2 = 30; + } + uint32 data2; }; BOOST_AUTO_TEST_CASE(inheritance_cast) { - Seele::RefPtr backCast; - { - Seele::RefPtr derived = new DerivedStruct(); - Seele::RefPtr base = derived; - BOOST_REQUIRE_EQUAL(base->data, 10); - backCast = base.cast(); - BOOST_REQUIRE_EQUAL(backCast->data, 10); - BOOST_REQUIRE_EQUAL(backCast->data2, 20); - } - BOOST_REQUIRE_EQUAL(backCast->data, 10); - BOOST_REQUIRE_EQUAL(backCast->data2, 20); + Seele::RefPtr backCast; + { + Seele::RefPtr derived = new DerivedStruct(); + Seele::RefPtr base = derived; + BOOST_REQUIRE_EQUAL(base->data, 10); + backCast = base.cast(); + BOOST_REQUIRE_EQUAL(backCast->data, 10); + BOOST_REQUIRE_EQUAL(backCast->data2, 20); + } + BOOST_REQUIRE_EQUAL(backCast->data, 10); + BOOST_REQUIRE_EQUAL(backCast->data2, 20); } BOOST_AUTO_TEST_CASE(unique_ptr) { - Seele::UniquePtr uptr = new TestStruct(); - Seele::UniquePtr uptr2 = std::move(uptr); - BOOST_REQUIRE_EQUAL(uptr2->data, 10); - BOOST_REQUIRE_EQUAL(uptr.isValid(), false); + Seele::UniquePtr uptr = new TestStruct(); + Seele::UniquePtr uptr2 = std::move(uptr); + BOOST_REQUIRE_EQUAL(uptr2->data, 10); + BOOST_REQUIRE_EQUAL(uptr.isValid(), false); } struct ThisReference { - ThisReference() - { - } - ~ThisReference() - { - data = 12; - } - Seele::RefPtr getRef() - { - return this; - } - uint32 data = 1; + ThisReference() + { + } + ~ThisReference() + { + data = 12; + } + Seele::RefPtr getRef() + { + return this; + } + uint32 data = 1; }; BOOST_AUTO_TEST_CASE(this_reference) { - Seele::RefPtr ptr2; - { - Seele::RefPtr ptr = new ThisReference(); - BOOST_REQUIRE_EQUAL(ptr->data, 1); - ptr2 = ptr->getRef(); - BOOST_REQUIRE_EQUAL(ptr2->data, 1); - ptr2->data = 5; - BOOST_REQUIRE_EQUAL(ptr->data, 5); - } - BOOST_REQUIRE_EQUAL(ptr2->data, 5); + Seele::RefPtr ptr2; + { + Seele::RefPtr ptr = new ThisReference(); + BOOST_REQUIRE_EQUAL(ptr->data, 1); + ptr2 = ptr->getRef(); + BOOST_REQUIRE_EQUAL(ptr2->data, 1); + ptr2->data = 5; + BOOST_REQUIRE_EQUAL(ptr->data, 5); + } + BOOST_REQUIRE_EQUAL(ptr2->data, 5); } BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test/Engine/EngineTest.h b/test/Engine/EngineTest.h index e2b46c5..9b55478 100644 --- a/test/Engine/EngineTest.h +++ b/test/Engine/EngineTest.h @@ -1,6 +1,5 @@ #pragma once #include -#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(); diff --git a/test/Engine/ThreadPool.cpp b/test/Engine/ThreadPool.cpp index e3b1416..bb2b8cb 100644 --- a/test/Engine/ThreadPool.cpp +++ b/test/Engine/ThreadPool.cpp @@ -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() \ No newline at end of file