From e5a4e57dbcbe3db8bab6d5dc23ed3566da661c76 Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Wed, 3 Dec 2025 09:40:45 -0500 Subject: [PATCH 1/5] Better thread pool --- include/wv/threading/ThreadPool.h | 26 +++++++---- src/threading/ThreadPool.cpp | 75 +++++++++++++++++-------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/include/wv/threading/ThreadPool.h b/include/wv/threading/ThreadPool.h index 92352ac..80991bf 100644 --- a/include/wv/threading/ThreadPool.h +++ b/include/wv/threading/ThreadPool.h @@ -1,27 +1,37 @@ #pragma once #include +#include #include #include +#include namespace WillowVox { + enum class Priority { + High = 0, + Medium = 1, + Low = 2, + Count = 3 + }; + class ThreadPool { public: - ThreadPool(int initialJobQueueCapacity); - ThreadPool() = default; + ThreadPool(); + ~ThreadPool(); void Start(int numThreads); - void QueueJob(const std::function& job, bool highPriority = false); - void Stop(); + void Enqueue(const std::function& job, Priority priority = Priority::Medium); private: void ThreadLoop(); - std::atomic m_shouldTerminate; - std::condition_variable m_mutexCondition; + // Queues for each priority + std::array>, static_cast(Priority::Count)> m_queues; + // Queue to wake up threads when a job is queued + moodycamel::BlockingConcurrentQueue m_signal; + std::vector m_threads; - moodycamel::ConcurrentQueue> m_lowPriorityJobs; - moodycamel::ConcurrentQueue> m_highPriorityJobs; + std::atomic m_shouldTerminate; }; } \ No newline at end of file diff --git a/src/threading/ThreadPool.cpp b/src/threading/ThreadPool.cpp index 2beb757..5a8b249 100644 --- a/src/threading/ThreadPool.cpp +++ b/src/threading/ThreadPool.cpp @@ -4,8 +4,23 @@ namespace WillowVox { - ThreadPool::ThreadPool(int initialJobQueueCapacity = 0) - : m_shouldTerminate(false), m_highPriorityJobs(initialJobQueueCapacity), m_lowPriorityJobs(initialJobQueueCapacity) {} + ThreadPool::ThreadPool() + : m_shouldTerminate(false) {} + + ThreadPool::~ThreadPool() + { + m_shouldTerminate = true; + + // Make sure all threads stop + for (int i = 0; i < m_threads.size() * 2; i++) + m_signal.enqueue(true); + + // Join all threads + for (std::thread& activeThread : m_threads) + activeThread.join(); + + m_threads.clear(); + } void ThreadPool::Start(int numThreads) { @@ -13,51 +28,43 @@ namespace WillowVox m_threads.emplace_back(std::thread(&ThreadPool::ThreadLoop, this)); } - void ThreadPool::QueueJob(const std::function& job, bool highPriority) + void ThreadPool::Enqueue(const std::function& job, Priority priority) { - { - if (highPriority) - m_highPriorityJobs.enqueue(job); - else - m_lowPriorityJobs.enqueue(job); - } - //m_mutexCondition.notify_one(); - } - - void ThreadPool::Stop() - { - m_shouldTerminate = true; - - m_mutexCondition.notify_all(); - for (std::thread& activeThread : m_threads) - activeThread.join(); - - m_threads.clear(); + // Enqueue the job + m_queues[static_cast(priority)].enqueue(job); + // Wake up a worker thread to run the job + m_signal.enqueue(true); } void ThreadPool::ThreadLoop() { + bool token; while (true) { - std::this_thread::sleep_for(std::chrono::milliseconds(5)); + // Wait for job to be enqueued + m_signal.wait_dequeue(token); + Logger::Log("Worker thread %d checking for jobs", std::this_thread::get_id()); + // Stop early if necessary + if (m_shouldTerminate) + return; + + // Get job to run std::function job; - { - /*m_mutexCondition.wait(lock, [this] { - return !m_highPriorityJobs.() || !m_lowPriorityJobs.empty() || m_shouldTerminate; - });*/ - if (m_shouldTerminate) - return; + bool found = false; - bool jobFound = m_highPriorityJobs.try_dequeue(job); - if (!jobFound) + for (int i = 0; i < static_cast(Priority::Count); i++) + { + if (m_queues[static_cast(Priority::High)].try_dequeue(job)) { - jobFound = m_lowPriorityJobs.try_dequeue(job); - if (!jobFound) - continue; + found = true; + break; } } - job(); + + // Run job if found + if (found && job) + job(); } } } \ No newline at end of file From b3b596c4b63734658bfbf61a154beaa6bae4361f Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Wed, 3 Dec 2025 10:03:44 -0500 Subject: [PATCH 2/5] Fixed thread pools and app destructor --- include/wv/app/App.h | 3 +++ src/threading/ThreadPool.cpp | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/wv/app/App.h b/include/wv/app/App.h index 508fd54..5db0e0d 100644 --- a/include/wv/app/App.h +++ b/include/wv/app/App.h @@ -9,6 +9,9 @@ namespace WillowVox class App { public: + App() = default; + virtual ~App() = default; + void Run(); virtual void Start() {} diff --git a/src/threading/ThreadPool.cpp b/src/threading/ThreadPool.cpp index 5a8b249..6104d78 100644 --- a/src/threading/ThreadPool.cpp +++ b/src/threading/ThreadPool.cpp @@ -43,8 +43,7 @@ namespace WillowVox { // Wait for job to be enqueued m_signal.wait_dequeue(token); - Logger::Log("Worker thread %d checking for jobs", std::this_thread::get_id()); - + // Stop early if necessary if (m_shouldTerminate) return; @@ -55,7 +54,7 @@ namespace WillowVox for (int i = 0; i < static_cast(Priority::Count); i++) { - if (m_queues[static_cast(Priority::High)].try_dequeue(job)) + if (m_queues[i].try_dequeue(job)) { found = true; break; From 2236fc0502d0b162cb30d478857e01e533f07905 Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Wed, 3 Dec 2025 12:48:56 -0500 Subject: [PATCH 3/5] Added comments to app and asset manager files --- include/wv/app/App.h | 7 +++++++ include/wv/app/EntryPoint.h | 4 ++++ include/wv/assets/AssetLoader.h | 3 +++ include/wv/assets/AssetManager.h | 7 +++++++ include/wv/assets/AssetProvider.h | 5 +++++ 5 files changed, 26 insertions(+) diff --git a/include/wv/app/App.h b/include/wv/app/App.h index 5db0e0d..51349dc 100644 --- a/include/wv/app/App.h +++ b/include/wv/app/App.h @@ -6,18 +6,25 @@ namespace WillowVox extern int appDefaultWindowX; extern int appDefaultWindowY; + // This is the base application class + // The project must create a subclass of this class App { public: App() = default; virtual ~App() = default; + // To be used by the engine. Starts the application void Run(); + // Runs at the start of the project virtual void Start() {} + // Runs every frame virtual void Update() {} + // Runs at the end of every frame for custom rendering code virtual void Render() {} + // Delta time between frames static float m_deltaTime; private: diff --git a/include/wv/app/EntryPoint.h b/include/wv/app/EntryPoint.h index 57c44f0..0af8a55 100644 --- a/include/wv/app/EntryPoint.h +++ b/include/wv/app/EntryPoint.h @@ -1,3 +1,7 @@ +// Entry point for the application +// This must be included in the main.cpp file of the project. +// CreateApp must be defined in the project. + #pragma once #include diff --git a/include/wv/assets/AssetLoader.h b/include/wv/assets/AssetLoader.h index fc74285..ff0d2c0 100644 --- a/include/wv/assets/AssetLoader.h +++ b/include/wv/assets/AssetLoader.h @@ -4,9 +4,12 @@ namespace WillowVox { + // If you want to support loading a new asset type T, + // you need to create a template specialization of this struct template struct AssetLoader { + // Load asset of type T from given name (e.g., file path) static std::shared_ptr Load(const std::string& name); }; } \ No newline at end of file diff --git a/include/wv/assets/AssetManager.h b/include/wv/assets/AssetManager.h index f5b095f..499e82e 100644 --- a/include/wv/assets/AssetManager.h +++ b/include/wv/assets/AssetManager.h @@ -6,11 +6,16 @@ namespace WillowVox { + // Manages loading and providing assets of various types class AssetManager { public: static AssetManager& GetInstance(); + // Get asset of type T by name + // Loads the asset if not already loaded + // Uses AssetLoader to load the asset if needed. If you want to support loading a new asset type T, + // you need to create a template specialization of AssetLoader template std::shared_ptr GetAsset(const std::string& name) { @@ -30,6 +35,8 @@ namespace WillowVox return provider->GetAsset(name); } + // Manually add asset of type T by name + // This is usually not needed unless you want to pre-load assets or load runtime-generated assets template void AddAsset(const std::string& name, std::shared_ptr asset) { diff --git a/include/wv/assets/AssetProvider.h b/include/wv/assets/AssetProvider.h index a5058f6..cbec33c 100644 --- a/include/wv/assets/AssetProvider.h +++ b/include/wv/assets/AssetProvider.h @@ -10,10 +10,13 @@ namespace WillowVox public: }; + // Provides assets of type T, loading them on demand template class AssetProvider : public IAssetProvider { public: + // Get asset of type T by name + // Loads the asset if not already loaded std::shared_ptr GetAsset(const std::string& name) { auto it = m_assets.find(name); @@ -27,6 +30,8 @@ namespace WillowVox } } + // Add asset of type T by name + // Used to manually add assets (e.g., pre-loaded or runtime-generated) void AddAsset(const std::string& name, std::shared_ptr asset) { m_assets.emplace(name, asset); From 2a71318b6a0c4ae1f7bd8c75aa4a33789d662a6e Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Wed, 3 Dec 2025 12:51:24 -0500 Subject: [PATCH 4/5] Added ocmments to some rendering code --- include/wv/rendering/ElementBuffer.h | 1 + include/wv/rendering/Shader.h | 1 + include/wv/rendering/VertexArrayObject.h | 1 + include/wv/rendering/VertexBuffer.h | 1 + 4 files changed, 4 insertions(+) diff --git a/include/wv/rendering/ElementBuffer.h b/include/wv/rendering/ElementBuffer.h index ea47e65..a3d8010 100644 --- a/include/wv/rendering/ElementBuffer.h +++ b/include/wv/rendering/ElementBuffer.h @@ -16,6 +16,7 @@ namespace WillowVox public: ElementBuffer(); ElementBuffer(ElementBuffer&& other) noexcept; + // Make sure destructor only runs on the main thread ~ElementBuffer(); void Bind(); diff --git a/include/wv/rendering/Shader.h b/include/wv/rendering/Shader.h index a9da18e..c2a9353 100644 --- a/include/wv/rendering/Shader.h +++ b/include/wv/rendering/Shader.h @@ -13,6 +13,7 @@ namespace WillowVox static std::shared_ptr FromSource(const char* vertexShaderCode, const char* fragmentShaderCode); Shader(unsigned int programId) : _programId(programId) {} + // Make sure destructor only runs on the main thread ~Shader(); void Bind(); diff --git a/include/wv/rendering/VertexArrayObject.h b/include/wv/rendering/VertexArrayObject.h index 81f3419..7320b08 100644 --- a/include/wv/rendering/VertexArrayObject.h +++ b/include/wv/rendering/VertexArrayObject.h @@ -9,6 +9,7 @@ namespace WillowVox { public: VertexArrayObject(); + // Make sure the destructor only runs on the main thread ~VertexArrayObject(); void Bind(); diff --git a/include/wv/rendering/VertexBuffer.h b/include/wv/rendering/VertexBuffer.h index 7f56c76..04a9740 100644 --- a/include/wv/rendering/VertexBuffer.h +++ b/include/wv/rendering/VertexBuffer.h @@ -22,6 +22,7 @@ namespace WillowVox public: VertexBuffer(); VertexBuffer(VertexBuffer&& other) noexcept; + // Make sure destructor only runs on the main thread ~VertexBuffer(); void Bind(); From a6d113626068e4edf15c7ec23355de9d06033e37 Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Sat, 13 Dec 2025 10:32:26 -0500 Subject: [PATCH 5/5] Changed int attribs to iattribs --- include/wv/wvpch.h | 1 + src/rendering/VertexBuffer.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/wv/wvpch.h b/include/wv/wvpch.h index 0ee13b7..16d6dbc 100644 --- a/include/wv/wvpch.h +++ b/include/wv/wvpch.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/rendering/VertexBuffer.cpp b/src/rendering/VertexBuffer.cpp index 5ad8411..ec6b0ca 100644 --- a/src/rendering/VertexBuffer.cpp +++ b/src/rendering/VertexBuffer.cpp @@ -46,22 +46,22 @@ namespace WillowVox glVertexAttribPointer(index, attribSize, GL_HALF_FLOAT, normalized, vertexSize, (void*)offset); break; case VertexBufferAttribType::INT32: - glVertexAttribPointer(index, attribSize, GL_INT, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_INT, vertexSize, (void*)offset); break; case VertexBufferAttribType::UINT32: - glVertexAttribPointer(index, attribSize, GL_UNSIGNED_INT, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_UNSIGNED_INT, vertexSize, (void*)offset); break; case VertexBufferAttribType::INT16: - glVertexAttribPointer(index, attribSize, GL_SHORT, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_SHORT, vertexSize, (void*)offset); break; case VertexBufferAttribType::UINT16: - glVertexAttribPointer(index, attribSize, GL_UNSIGNED_SHORT, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_UNSIGNED_SHORT, vertexSize, (void*)offset); break; case VertexBufferAttribType::INT8: - glVertexAttribPointer(index, attribSize, GL_BYTE, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_BYTE, vertexSize, (void*)offset); break; case VertexBufferAttribType::UINT8: - glVertexAttribPointer(index, attribSize, GL_UNSIGNED_BYTE, normalized, vertexSize, (void*)offset); + glVertexAttribIPointer(index, attribSize, GL_UNSIGNED_BYTE, vertexSize, (void*)offset); break; } glEnableVertexAttribArray(index);