Merge branch 'master' into master

This commit is contained in:
WSAL Evan
2026-03-28 10:24:48 -04:00
committed by GitHub
13 changed files with 100 additions and 48 deletions
+10
View File
@@ -12,15 +12,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(const InputState& input) {}
// Runs at the end of every frame for custom rendering code
virtual void Render() {}
// Delta time between frames
static float m_deltaTime;
// Access to platform and graphics context
+4
View File
@@ -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 <wv/app/App.h>
+3
View File
@@ -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<typename T>
struct AssetLoader
{
// Load asset of type T from given name (e.g., file path)
static std::shared_ptr<T> Load(const std::string& name);
};
}
+7
View File
@@ -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<T> 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<typename T>
std::shared_ptr<T> 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<typename T>
void AddAsset(const std::string& name, std::shared_ptr<T> asset)
{
+5
View File
@@ -10,10 +10,13 @@ namespace WillowVox
public:
};
// Provides assets of type T, loading them on demand
template<typename T>
class AssetProvider : public IAssetProvider
{
public:
// Get asset of type T by name
// Loads the asset if not already loaded
std::shared_ptr<T> 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<T> asset)
{
m_assets.emplace(name, asset);
+1
View File
@@ -16,6 +16,7 @@ namespace WillowVox
public:
ElementBuffer();
ElementBuffer(ElementBuffer&& other) noexcept;
// Make sure destructor only runs on the main thread
~ElementBuffer();
void Bind();
+1
View File
@@ -13,6 +13,7 @@ namespace WillowVox
static std::shared_ptr<Shader> 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();
+1
View File
@@ -9,6 +9,7 @@ namespace WillowVox
{
public:
VertexArrayObject();
// Make sure the destructor only runs on the main thread
~VertexArrayObject();
void Bind();
+1
View File
@@ -22,6 +22,7 @@ namespace WillowVox
public:
VertexBuffer();
VertexBuffer(VertexBuffer&& other) noexcept;
// Make sure destructor only runs on the main thread
~VertexBuffer();
void Bind();
+18 -8
View File
@@ -1,27 +1,37 @@
#pragma once
#include <concurrentqueue.h>
#include <blockingconcurrentqueue.h>
#include <wv/wvpch.h>
#include <atomic>
#include <array>
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<void()>& job, bool highPriority = false);
void Stop();
void Enqueue(const std::function<void()>& job, Priority priority = Priority::Medium);
private:
void ThreadLoop();
std::atomic<bool> m_shouldTerminate;
std::condition_variable m_mutexCondition;
// Queues for each priority
std::array<moodycamel::ConcurrentQueue<std::function<void()>>, static_cast<int>(Priority::Count)> m_queues;
// Queue to wake up threads when a job is queued
moodycamel::BlockingConcurrentQueue<bool> m_signal;
std::vector<std::thread> m_threads;
moodycamel::ConcurrentQueue<std::function<void()>> m_lowPriorityJobs;
moodycamel::ConcurrentQueue<std::function<void()>> m_highPriorityJobs;
std::atomic<bool> m_shouldTerminate;
};
}
+1
View File
@@ -5,6 +5,7 @@
#include <glm/gtc/type_ptr.hpp>
#include <vector>
#include <array>
#include <queue>
#include <unordered_map>
#include <string>
+6 -6
View File
@@ -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);
+42 -34
View File
@@ -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,49 +28,42 @@ namespace WillowVox
m_threads.emplace_back(std::thread(&ThreadPool::ThreadLoop, this));
}
void ThreadPool::QueueJob(const std::function<void()>& job, bool highPriority)
void ThreadPool::Enqueue(const std::function<void()>& 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<int>(priority)].enqueue(job);
// Wake up a worker thread to run the job
m_signal.enqueue(true);
}
void ThreadPool::ThreadLoop()
{
bool token;
while (true)
{
std::function<void()> job;
{
/*m_mutexCondition.wait(lock, [this] {
return !m_highPriorityJobs.() || !m_lowPriorityJobs.empty() || m_shouldTerminate;
});*/
if (m_shouldTerminate)
return;
// Wait for job to be enqueued
m_signal.wait_dequeue(token);
// Stop early if necessary
if (m_shouldTerminate)
return;
bool jobFound = m_highPriorityJobs.try_dequeue(job);
if (!jobFound)
// Get job to run
std::function<void()> job;
bool found = false;
for (int i = 0; i < static_cast<int>(Priority::Count); i++)
{
if (m_queues[i].try_dequeue(job))
{
jobFound = m_lowPriorityJobs.try_dequeue(job);
if (!jobFound)
continue;
found = true;
break;
}
}
job();
// Run job if found
if (found && job)
job();
}
}
}