Skip to content

Commit

Permalink
Added: Scheduler = task multithreading.
Browse files Browse the repository at this point in the history
  • Loading branch information
WillisMedwell committed Mar 13, 2024
1 parent 32522d0 commit 8b4f1cc
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 407 deletions.
5 changes: 4 additions & 1 deletion code/.clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ CheckOptions:
- { key: readability-identifier-naming.PrivateMemberPrefix, value: _ }
- { key: readability-identifier-naming.StaticMemberPrefix, value: _ }
- { key: readability-identifier-naming.LocalConstantCase, value: lower_case }
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.GlobalConstantCase, value: snake_case }
- { key: readability-identifier-naming.ConstexprVariableCase, value: snake_case }
- { key: readability-identifier-naming.ConstexprMethodCase, value: snake_case }
- { key: readability-identifier-naming.ConstexprFunctionCase, value: snake_case }
- { key: readability-identifier-naming.EnumConstantCase, value: lower_case }


44 changes: 42 additions & 2 deletions code/Demos/src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,45 @@ struct IsoLogic {

Media::Sound sound = Media::Sound::create("assets/background_sound.wav").on_error_panic().value_move();

auto scheduler = std::move(Core::Scheduler::create(2).value());

scheduler.add_task([]() {
Media::FontAtlas::create("assets/RobotoMono.ttf", 500).on_error_panic().value().atlas_image().save_to_disk("RobotoMonoAtlas.png");
});

std::mutex sound_mutex;
std::optional<Media::Sound> sound_2;
scheduler.add_task([&]() {
auto create_sound_result = Media::Sound::create("assets/background_sound.wav");

sound_mutex.lock();
sound_2.emplace(create_sound_result.on_error_panic().value_move());
sound_mutex.unlock();
});

std::mutex model_mutex;
std::optional<Model::Static> model_data_2;
scheduler.add_task([&]() {
auto model_data = Utily::FileReader::load_entire_file("assets/teapot.obj").on_error_panic().value_move();
auto model_decode_result = Model::decode_as_static_model(model_data, ".obj");

model_mutex.lock();
model_data_2.emplace(model_decode_result.on_error_panic().value_move());
model_mutex.unlock();
});

std::mutex image_mutex;
std::optional<Media::Image> image_2;
scheduler.add_task([&]() {
auto image_result = Media::Image::create("assets/texture.png");

image_mutex.lock();
image_2.emplace(image_result.on_error_panic().value_move());
image_mutex.unlock();
});

scheduler.launch_threads();

auto res = audio.load_sound_into_buffer(sound).on_error(print_then_quit);

data.sound_buffer = res.value();
Expand All @@ -436,10 +475,11 @@ struct IsoLogic {
.on_error_panic()
.value_move());

Media::FontAtlas::create("assets/RobotoMono.ttf", 500).on_error_panic().value().atlas_image().save_to_disk("RobotoMonoAtlas.png");

data.instance_renderer.init(data.resource_manager, model, image);
data.source_handle = audio.play_sound(data.sound_buffer, { 5, 0, 0 }).on_error(print_then_quit).value();

scheduler.wait_for_threads();

data.start_time = std::chrono::high_resolution_clock::now();
}

Expand Down
4 changes: 3 additions & 1 deletion code/Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ if(DEFINED EMSCRIPTEN)
"$<$<CONFIG:Debug>:-O2;-g3;-sDEMANGLE_SUPPORT=1;-sFORCE_FILESYSTEM=1;-sASSERTIONS=1;-sSAFE_HEAP=1;-sSTACK_OVERFLOW_CHECK=2;-sNO_DISABLE_EXCEPTION_CATCHING;-Wno-unused-command-line-argument;-fno-inline-functions;-sEXIT_RUNTIME=1>"
"$<$<CONFIG:Release>:-Oz;-sGL_FFP_ONLY;-msimd128;-mrelaxed-simd;-msse;-msse2;-msse3;-msse4.1;-Wno-unused-command-line-argument;-sFORCE_FILESYSTEM=1>"
)
target_link_options(Engine PUBLIC -sUSE_WEBGL2=1 -sUSE_GLFW=3 -sFULL_ES3=1 -sFULL_ES2=1 -Wno-unused-command-line-argument -sALLOW_MEMORY_GROWTH)
#target_compile_options(Engine PUBLIC -sUSE_PTHREADS=1)
target_link_options(Engine PUBLIC -sUSE_WEBGL2=1 -sUSE_GLFW=3 -sFULL_ES3=1 -sFULL_ES2=1 -Wno-unused-command-line-argument -sALLOW_MEMORY_GROWTH) #-sUSE_PTHREADS=1)

else()
find_package(OpenGL REQUIRED)
find_package(OpenAL CONFIG REQUIRED)
Expand Down
3 changes: 0 additions & 3 deletions code/Engine/include/App/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ class App
}
auto stop() -> void {
if (!_has_stopped) {
Profiler::Timer timer("App::stop()", { "App" });
_logic.stop(_data);
_renderer.stop();
_audio.stop();
Expand Down Expand Up @@ -113,15 +112,13 @@ class App
}
_context.swap_buffers();
}

auto poll_events() -> void {
Profiler::Timer timer("App::poll_events()", { "App" });
this->_context.poll_events();
}
auto is_running() -> bool {
return _has_init && !_context.should_close() && !_state.should_close;
}

~App() {
stop();
}
Expand Down
3 changes: 3 additions & 0 deletions code/Engine/include/Core/AudioManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ namespace Core {
};
struct Source {
uint32_t id = std::numeric_limits<uint32_t>::max();
glm::vec3 pos = { 0, 0, 0 };
glm::vec3 vel = { 0, 0, 0 };
std::optional<BufferHandle> attached_buffer = std::nullopt;
std::optional<std::chrono::steady_clock::time_point> expected_finish = std::nullopt;
};
Expand All @@ -75,4 +77,5 @@ namespace Core {
void stop_buffers();
void stop_sources();
};

}
6 changes: 3 additions & 3 deletions code/Engine/include/Core/Core.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

namespace Core {
class Fence;
class IndexBuffer;
class InputManager;
class OpenglContext;
Expand All @@ -12,9 +11,9 @@ namespace Core {
class FrameBuffer;
class ScreenFrameBuffer;
class AudioManager;
class Scheduler;
}

#include "Fence.hpp"
#include "IndexBuffer.hpp"
#include "Input.hpp"
#include "OpenglContext.hpp"
Expand All @@ -24,4 +23,5 @@ namespace Core {
#include "VertexBuffer.hpp"
#include "VertexBufferLayout.hpp"
#include "FrameBuffer.hpp"
#include "AudioManager.hpp"
#include "AudioManager.hpp"
#include "Scheduler.hpp"
53 changes: 0 additions & 53 deletions code/Engine/include/Core/Fence.hpp

This file was deleted.

139 changes: 139 additions & 0 deletions code/Engine/include/Core/Scheduler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include <algorithm>
#include <array>
#include <bit>
#include <functional>
#include <iterator>
#include <optional>
#include <stdexcept>
#include <thread>
#include <variant>
#include <vector>

#include "Config.hpp"

namespace Core {

class Scheduler
{
public:
using SimpleFunctionPtr = void (*)();
using Task = std::variant<SimpleFunctionPtr, std::function<void()>>;

static auto create(size_t num_threads = std::thread::hardware_concurrency())
-> std::optional<Scheduler> {
if (OptAtomicSize temp = 1; !temp.value().is_lock_free()) {
throw std::runtime_error(
"This has lock, as such it cannot be moved. Therefore the move "
"semantics workaround is invalidated.");
}
if constexpr (Config::PLATFORM == Config::TargetPlatform::web) {
num_threads = 0;
}

return Scheduler(M {
.tasks = {},
.threads = {},
.num_threads = num_threads,
.current_task_buffer = null_opt_atomic(),
});
}

void add_task(SimpleFunctionPtr task) {
if (get_current_task().has_value()) {
throw std::runtime_error("trying to add task when already lauched");
}
_m.tasks.emplace_back(task);
}

void add_task(Task task) {
if (get_current_task().has_value()) {
throw std::runtime_error("trying to add task when already lauched");
}
_m.tasks.emplace_back(task);
}

void launch_threads() {
if (get_current_task().has_value()) {
return;
}

get_current_task().emplace(0);

auto execute = [this]() {
for (;;) {
auto task_id =
get_current_task()->fetch_add(1, std::memory_order_seq_cst);

bool has_tasks_remaining = task_id < _m.tasks.size();

if (has_tasks_remaining) {
std::string timer_name = "Scheduler::Task(" + std::to_string(task_id) + ")";
Profiler::Timer timer(timer_name);
auto& task = _m.tasks.at(task_id);
std::visit([](auto& task) { task(); }, task);
} else {
return;
}
}
};
auto start_thread = [&]() { return std::thread(execute); };

// start the other threads
std::generate_n(std::back_inserter(_m.threads), _m.num_threads, start_thread);
}

void wait_for_threads() {
// get the main thread going too.
[this]() {
for (;;) {
auto task_id =
get_current_task()->fetch_add(1, std::memory_order_seq_cst);

bool has_tasks_remaining = task_id < _m.tasks.size();

if (has_tasks_remaining) {
auto& task = _m.tasks.at(task_id);
std::visit([](auto& task) { task(); }, task);
} else {
return;
}
}
}();

std::ranges::for_each(_m.threads, &std::thread::join);

_m.threads.resize(0);
_m.tasks.resize(0);
get_current_task() = std::nullopt;
}

Scheduler(Scheduler&&) = default;

private:
using OptAtomicSize = std::optional<std::atomic<size_t>>;
using OptAtomicSizeBuffer = std::array<std::byte, sizeof(OptAtomicSize)>;

constexpr static auto null_opt_atomic = []() {
OptAtomicSize empty = std::nullopt;
OptAtomicSizeBuffer buffer =
*reinterpret_cast<OptAtomicSizeBuffer*>(&empty);
return buffer;
};

struct M {
std::vector<Task> tasks;
std::vector<std::thread> threads;
size_t num_threads;
alignas(OptAtomicSize) OptAtomicSizeBuffer current_task_buffer;
} _m;

auto get_current_task() -> OptAtomicSize& {
return *reinterpret_cast<OptAtomicSize*>(_m.current_task_buffer.data());
}

explicit Scheduler(M&& m)
: _m(std::move(m)) { }

Scheduler(const Scheduler&) = delete;
};
}
1 change: 0 additions & 1 deletion code/Engine/include/Core/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <Utily/Utily.hpp>

#include "Core/Fence.hpp"
#include "Media/Image.hpp"
#include "Config.hpp"

Expand Down
Loading

0 comments on commit 8b4f1cc

Please sign in to comment.