Merge pull request 'feat/165-gui' (#168) from feat/165-gui into main
Reviewed-on: #168
This commit is contained in:
commit
ab9e7f4a3d
@ -6,6 +6,7 @@ target_link_libraries(nf7_core
|
||||
luajit
|
||||
nf7_config
|
||||
nf7_iface
|
||||
OpenGL
|
||||
SDL2
|
||||
sqlite
|
||||
uvw
|
||||
@ -13,6 +14,7 @@ target_link_libraries(nf7_core
|
||||
target_sources(nf7_core
|
||||
PRIVATE
|
||||
gl3/context.cc
|
||||
imgui/context.cc
|
||||
luajit/context.cc
|
||||
luajit/lambda.cc
|
||||
luajit/thread.cc
|
||||
@ -22,6 +24,8 @@ target_sources(nf7_core
|
||||
version.cc
|
||||
PUBLIC
|
||||
gl3/context.hh
|
||||
imgui/context.hh
|
||||
imgui/driver.hh
|
||||
luajit/context.hh
|
||||
luajit/lambda.hh
|
||||
luajit/thread.hh
|
||||
@ -44,6 +48,9 @@ add_executable(nf7_core_test)
|
||||
target_sources(nf7_core_test
|
||||
PRIVATE
|
||||
gl3/context_test.cc
|
||||
imgui/context_test.cc
|
||||
imgui/context_test.hh
|
||||
imgui/driver_test.hh
|
||||
luajit/context_test.cc
|
||||
luajit/context_test.hh
|
||||
luajit/lambda_test.cc
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <typeindex>
|
||||
@ -24,15 +25,22 @@ namespace nf7::core::test {
|
||||
|
||||
class EnvFixture : public ::testing::Test {
|
||||
public:
|
||||
EnvFixture() = delete;
|
||||
explicit EnvFixture(SimpleEnv::FactoryMap&& fmap) noexcept
|
||||
: fmap_(std::move(fmap)) { }
|
||||
EnvFixture() = default;
|
||||
|
||||
protected:
|
||||
template <typename I>
|
||||
void Install(const std::shared_ptr<I>& o) {
|
||||
fmap_.emplace(typeid(I), [o](auto&) { return o; });
|
||||
}
|
||||
template <typename I>
|
||||
void Install(std::function<std::shared_ptr<I>(Env&)>&& factory) {
|
||||
fmap_.emplace(typeid(I), std::move(factory));
|
||||
}
|
||||
template <typename I, typename T>
|
||||
void Install() {
|
||||
fmap_.emplace(
|
||||
typeid(I), [](auto& env) { return std::make_shared<T>(env); });
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
@ -120,9 +128,8 @@ class EnvFixtureWithTasking : public EnvFixture {
|
||||
};
|
||||
|
||||
public:
|
||||
explicit EnvFixtureWithTasking(SimpleEnv::FactoryMap&& fmap = {})
|
||||
: EnvFixture(std::move(fmap)),
|
||||
clock_(std::make_shared<Clock>()),
|
||||
EnvFixtureWithTasking()
|
||||
: clock_(std::make_shared<Clock>()),
|
||||
sq_(std::make_shared<SimpleTaskQueue<SyncTask>>()),
|
||||
aq_(std::make_shared<SimpleTaskQueue<AsyncTask>>()),
|
||||
ad_(*this) {
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "core/gl3/context.hh"
|
||||
|
||||
#include "iface/common/exception.hh"
|
||||
#include "iface/subsys/clock.hh"
|
||||
#include "iface/subsys/logger.hh"
|
||||
#include "iface/subsys/parallelism.hh"
|
||||
|
||||
#include "core/logger.hh"
|
||||
@ -11,138 +13,151 @@ using namespace std::literals;
|
||||
|
||||
namespace nf7::core::gl3 {
|
||||
|
||||
Context::Context(Env& env, const std::shared_ptr<Impl>& impl)
|
||||
class Context::Impl : public std::enable_shared_from_this<Impl> {
|
||||
private:
|
||||
static constexpr const auto kPollingInterval = 17ms;
|
||||
|
||||
public:
|
||||
Impl(Env& env, Context& ctx)
|
||||
try : clock_(env.Get<subsys::Clock>()),
|
||||
concurrency_(env.Get<subsys::Concurrency>()),
|
||||
logger_(env.GetOr<subsys::Logger>(NullLogger::kInstance)),
|
||||
ctx_(&ctx) {
|
||||
sdl_ = 0 == SDL_Init(SDL_INIT_VIDEO);
|
||||
if (!sdl_) {
|
||||
throw Exception {"SDL init failure"};
|
||||
}
|
||||
SetUpGL();
|
||||
SetUpWindow();
|
||||
} catch (const Exception&) {
|
||||
TearDown();
|
||||
throw;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~Impl() = default;
|
||||
Impl(const Impl&) = delete;
|
||||
Impl(Impl&&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
Impl& operator=(Impl&&) = delete;
|
||||
|
||||
public:
|
||||
TaskContext MakeContext() const noexcept {
|
||||
return TaskContext {win_, gl_};
|
||||
}
|
||||
void SchedulePolling() noexcept {
|
||||
if (nullptr != ctx_) {
|
||||
concurrency_->Push(nf7::SyncTask {
|
||||
clock_->now() + kPollingInterval,
|
||||
[this, self = shared_from_this()](auto&) { Poll(); },
|
||||
});
|
||||
}
|
||||
}
|
||||
void ScheduleTearDown() noexcept {
|
||||
ctx_ = nullptr;
|
||||
concurrency_->Exec(
|
||||
[this, self = shared_from_this()](auto&) { TearDown(); });
|
||||
}
|
||||
void Push(Task&& task) noexcept {
|
||||
const auto after = task.after();
|
||||
concurrency_->Push(SyncTask {
|
||||
after,
|
||||
[this, self = shared_from_this(), task = std::move(task)](auto&) mutable {
|
||||
auto ctx = MakeContext();
|
||||
task(ctx);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void Poll() noexcept {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (nullptr != ctx_) {
|
||||
ctx_->Notify(SDL_Event {e});
|
||||
}
|
||||
}
|
||||
SchedulePolling();
|
||||
}
|
||||
|
||||
private:
|
||||
void SetUpGL() noexcept {
|
||||
# if defined(__APPLE__)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
# else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
# endif
|
||||
}
|
||||
void SetUpWindow() {
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
constexpr auto flags {
|
||||
SDL_WINDOW_OPENGL
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI
|
||||
};
|
||||
win_ = SDL_CreateWindow(
|
||||
"Dear ImGui SDL2+OpenGL3 example",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
1280, 720,
|
||||
flags);
|
||||
if (nullptr == win_) {
|
||||
throw Exception {"failed to create new window"};
|
||||
}
|
||||
|
||||
gl_ = SDL_GL_CreateContext(win_);
|
||||
if (nullptr == gl_) {
|
||||
throw Exception {"failed to create new GL context"};
|
||||
}
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
void TearDown() noexcept {
|
||||
if (nullptr != gl_) {
|
||||
SDL_GL_DeleteContext(gl_);
|
||||
}
|
||||
if (nullptr != win_) {
|
||||
SDL_DestroyWindow(win_);
|
||||
}
|
||||
if (sdl_) {
|
||||
SDL_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<subsys::Clock> clock_;
|
||||
const std::shared_ptr<subsys::Concurrency> concurrency_;
|
||||
const std::shared_ptr<subsys::Logger> logger_;
|
||||
|
||||
Context* ctx_;
|
||||
|
||||
bool sdl_ {false};
|
||||
SDL_Window* win_ {nullptr};
|
||||
void* gl_ {nullptr};
|
||||
};
|
||||
|
||||
Context::Context(Env& env)
|
||||
try : subsys::Interface("nf7::core::gl3::Context"),
|
||||
impl_(impl? impl: std::make_shared<Impl>(env)) {
|
||||
impl_->Main();
|
||||
impl_(std::make_shared<Impl>(env, *this)) {
|
||||
impl_->SchedulePolling();
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw MemoryException {};
|
||||
}
|
||||
Context::~Context() noexcept {
|
||||
impl_->Exit();
|
||||
impl_->ScheduleTearDown();
|
||||
}
|
||||
void Context::Push(Task&& task) noexcept {
|
||||
impl_->Push(std::move(task));
|
||||
}
|
||||
|
||||
|
||||
class Context::Impl::TaskDriver final {
|
||||
public:
|
||||
TaskDriver() = delete;
|
||||
explicit TaskDriver(const std::shared_ptr<subsys::Logger>& logger,
|
||||
Task::Time time) noexcept
|
||||
: logger_(logger), time_(time) { }
|
||||
|
||||
public:
|
||||
void BeginBusy() noexcept { }
|
||||
void EndBusy() noexcept { }
|
||||
|
||||
void Drive(Task&& task) noexcept
|
||||
try {
|
||||
task(ctx_);
|
||||
} catch (Exception& e) {
|
||||
logger_->Warn("GL3 task caused an exception");
|
||||
}
|
||||
|
||||
public:
|
||||
Task::Time tick() const noexcept { return time_; }
|
||||
bool nextIdleInterruption() const noexcept { return true; }
|
||||
bool nextTaskInterruption() const noexcept { return false; }
|
||||
|
||||
private:
|
||||
const std::shared_ptr<subsys::Logger> logger_;
|
||||
const Task::Time time_;
|
||||
|
||||
TaskContext ctx_ {};
|
||||
};
|
||||
|
||||
Context::Impl::Impl(Env& env)
|
||||
try : clock_(env.Get<subsys::Clock>()),
|
||||
concurrency_(env.Get<subsys::Concurrency>()),
|
||||
logger_(env.GetOr<subsys::Logger>(NullLogger::kInstance)) {
|
||||
sdl_ = 0 == SDL_Init(SDL_INIT_VIDEO);
|
||||
if (!sdl_) {
|
||||
throw Exception {"SDL init failure"};
|
||||
}
|
||||
SetUpGL();
|
||||
SetUpWindow();
|
||||
} catch (const Exception&) {
|
||||
TearDown();
|
||||
throw;
|
||||
}
|
||||
|
||||
void Context::Impl::Main() noexcept {
|
||||
const bool alive = Update();
|
||||
const auto now = clock_->now();
|
||||
|
||||
TaskDriver driver {logger_, now};
|
||||
tasq_.Drive(driver);
|
||||
|
||||
if (alive_ && alive) {
|
||||
concurrency_->Push(nf7::SyncTask {
|
||||
now + 33ms,
|
||||
[wself = weak_from_this()](auto&) {
|
||||
if (auto self = wself.lock()) { self->Main(); }
|
||||
},
|
||||
});
|
||||
} else {
|
||||
TearDown();
|
||||
}
|
||||
}
|
||||
|
||||
void Context::Impl::SetUpGL() noexcept {
|
||||
# if defined(__APPLE__)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
# else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
# endif
|
||||
}
|
||||
|
||||
void Context::Impl::SetUpWindow() {
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
constexpr auto flags {
|
||||
SDL_WINDOW_OPENGL
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI
|
||||
};
|
||||
win_ = SDL_CreateWindow(
|
||||
"Dear ImGui SDL2+OpenGL3 example",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
1280, 720,
|
||||
flags);
|
||||
if (nullptr == win_) {
|
||||
throw Exception {"failed to create new window"};
|
||||
}
|
||||
|
||||
gl_ = SDL_GL_CreateContext(win_);
|
||||
if (nullptr == gl_) {
|
||||
throw Exception {"failed to create new GL context"};
|
||||
}
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
|
||||
void Context::Impl::TearDown() noexcept {
|
||||
if (nullptr != gl_) {
|
||||
SDL_GL_DeleteContext(gl_);
|
||||
}
|
||||
if (nullptr != win_) {
|
||||
SDL_DestroyWindow(win_);
|
||||
}
|
||||
if (sdl_) {
|
||||
SDL_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nf7::core::gl3
|
||||
|
@ -6,10 +6,9 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "iface/subsys/clock.hh"
|
||||
#include "iface/common/observer.hh"
|
||||
#include "iface/subsys/concurrency.hh"
|
||||
#include "iface/subsys/interface.hh"
|
||||
#include "iface/subsys/logger.hh"
|
||||
#include "iface/env.hh"
|
||||
|
||||
|
||||
@ -17,19 +16,32 @@ namespace nf7::core::gl3 {
|
||||
|
||||
class TaskContext final {
|
||||
public:
|
||||
TaskContext() = default;
|
||||
TaskContext() = delete;
|
||||
TaskContext(SDL_Window* win, void* gl) noexcept
|
||||
: win_(win), gl_(gl) { }
|
||||
|
||||
TaskContext(const TaskContext&) = delete;
|
||||
TaskContext(TaskContext&&) = delete;
|
||||
TaskContext& operator=(const TaskContext&) = delete;
|
||||
TaskContext& operator=(TaskContext&&) = delete;
|
||||
|
||||
public:
|
||||
SDL_Window* win() const noexcept { return win_; }
|
||||
void* gl() const noexcept { return gl_; }
|
||||
|
||||
private:
|
||||
SDL_Window* const win_;
|
||||
void* const gl_;
|
||||
};
|
||||
|
||||
using Task = nf7::Task<TaskContext&>;
|
||||
using TaskQueue = nf7::TaskQueue<Task>;
|
||||
|
||||
|
||||
class Context : public subsys::Interface, public TaskQueue {
|
||||
class Context :
|
||||
public subsys::Interface,
|
||||
public Observer<SDL_Event>::Target,
|
||||
public TaskQueue {
|
||||
public:
|
||||
# if defined(__APPLE__)
|
||||
static constexpr const char* kGlslVersion = "#version 150";
|
||||
@ -41,67 +53,15 @@ class Context : public subsys::Interface, public TaskQueue {
|
||||
class Impl;
|
||||
|
||||
public:
|
||||
explicit Context(
|
||||
Env&,
|
||||
const std::shared_ptr<Impl>& = nullptr);
|
||||
explicit Context(Env&);
|
||||
~Context() noexcept override;
|
||||
|
||||
public:
|
||||
// THREAD-SAFE
|
||||
void Push(Task&&) noexcept override;
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
class Context::Impl : public std::enable_shared_from_this<Impl> {
|
||||
public:
|
||||
class TaskDriver;
|
||||
friend class Context;
|
||||
|
||||
public:
|
||||
explicit Impl(Env&);
|
||||
virtual ~Impl() = default;
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl(Impl&&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
Impl& operator=(Impl&&) = delete;
|
||||
|
||||
protected:
|
||||
virtual bool Update() noexcept {
|
||||
SDL_Event ev;
|
||||
while (SDL_PollEvent(&ev)) { }
|
||||
|
||||
SDL_GL_MakeCurrent(win_, gl_);
|
||||
SDL_GL_SwapWindow(win_);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
SDL_Window* win() const noexcept { return win_; }
|
||||
void* gl() const noexcept { return gl_; }
|
||||
|
||||
private:
|
||||
void Exit() noexcept { alive_ = false; }
|
||||
void Push(Task&& task) noexcept { tasq_.Push(std::move(task)); }
|
||||
void Main() noexcept;
|
||||
|
||||
private:
|
||||
void SetUpGL() noexcept;
|
||||
void SetUpWindow();
|
||||
void TearDown() noexcept;
|
||||
|
||||
private:
|
||||
const std::shared_ptr<subsys::Clock> clock_;
|
||||
const std::shared_ptr<subsys::Concurrency> concurrency_;
|
||||
const std::shared_ptr<subsys::Logger> logger_;
|
||||
|
||||
nf7::SimpleTaskQueue<Task> tasq_;
|
||||
bool alive_ {true};
|
||||
|
||||
bool sdl_ {false};
|
||||
SDL_Window* win_ {nullptr};
|
||||
void* gl_ {nullptr};
|
||||
};
|
||||
|
||||
} // namespace nf7::core::gl3
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "iface/env.hh"
|
||||
|
||||
#include "core/env_test.hh"
|
||||
@ -15,12 +17,11 @@ namespace nf7::core::gl3::test {
|
||||
class ContextFixture : public nf7::core::test::EnvFixtureWithTasking {
|
||||
public:
|
||||
ContextFixture() noexcept
|
||||
: EnvFixtureWithTasking({
|
||||
nf7::SimpleEnv::MakePair<Context, Context>(),
|
||||
}),
|
||||
skip_(nullptr == std::getenv("NF7_TEST_GL3")) { }
|
||||
: skip_(nullptr == std::getenv("NF7_TEST_GL3")) {
|
||||
Install<Context, Context>();
|
||||
}
|
||||
|
||||
public:
|
||||
protected:
|
||||
void SetUp() override {
|
||||
if (skip_) {
|
||||
GTEST_SKIP();
|
||||
|
163
core/imgui/context.cc
Normal file
163
core/imgui/context.cc
Normal file
@ -0,0 +1,163 @@
|
||||
// No copyright
|
||||
#include "core/imgui/context.hh"
|
||||
|
||||
#include <SDL_opengl.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <imgui_impl_sdl2.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "iface/common/exception.hh"
|
||||
#include "iface/common/observer.hh"
|
||||
#include "iface/subsys/clock.hh"
|
||||
|
||||
#include "core/gl3/context.hh"
|
||||
|
||||
|
||||
namespace nf7::core::imgui {
|
||||
|
||||
class Context::Impl final : public std::enable_shared_from_this<Impl> {
|
||||
private:
|
||||
static constexpr auto kUpdateInterval = std::chrono::milliseconds {33};
|
||||
|
||||
private:
|
||||
class EventQueue final : public Observer<SDL_Event> {
|
||||
public:
|
||||
using Observer<SDL_Event>::Observer;
|
||||
|
||||
public:
|
||||
EventQueue(const EventQueue&) = delete;
|
||||
EventQueue(EventQueue&&) = delete;
|
||||
EventQueue& operator=(const EventQueue&) = delete;
|
||||
EventQueue& operator=(EventQueue&&) = delete;
|
||||
|
||||
public:
|
||||
std::vector<SDL_Event> Take() noexcept { return std::move(items_); }
|
||||
|
||||
private:
|
||||
void Notify(const SDL_Event& e) noexcept override { items_.push_back(e); }
|
||||
|
||||
private:
|
||||
std::vector<SDL_Event> items_;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Impl(Env& env)
|
||||
: clock_(env.Get<subsys::Clock>()),
|
||||
gl3_(env.Get<gl3::Context>()),
|
||||
events_(std::make_unique<EventQueue>(*gl3_)),
|
||||
imgui_(ImGui::CreateContext()) {
|
||||
}
|
||||
Impl(const Impl&) = delete;
|
||||
Impl(Impl&&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
Impl& operator=(Impl&&) = delete;
|
||||
|
||||
public:
|
||||
void ScheduleStart() noexcept {
|
||||
gl3_->Exec([this, self = shared_from_this()](auto& t) { Start(t); });
|
||||
}
|
||||
void ScheduleTearDown() noexcept {
|
||||
gl3_->Exec([this, self = shared_from_this()](auto& t) { TearDown(t); });
|
||||
}
|
||||
|
||||
const std::shared_ptr<Driver>& Register(const std::shared_ptr<Driver>& driver)
|
||||
try {
|
||||
drivers_.emplace_back(driver);
|
||||
return driver;
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw MemoryException {};
|
||||
}
|
||||
|
||||
private:
|
||||
void Start(gl3::TaskContext& t) noexcept {
|
||||
ImGui::SetCurrentContext(imgui_);
|
||||
ImGui_ImplSDL2_InitForOpenGL(t.win(), t.gl());
|
||||
ImGui_ImplOpenGL3_Init(gl3::Context::kGlslVersion);
|
||||
Update(t);
|
||||
}
|
||||
void Update(gl3::TaskContext& t) noexcept {
|
||||
ImGui::SetCurrentContext(imgui_);
|
||||
|
||||
// get active drivers and drop dead ones
|
||||
std::vector<std::shared_ptr<Driver>> drivers;
|
||||
for (auto itr = drivers_.begin(); itr != drivers_.end();) {
|
||||
if (auto driver = itr->lock()) {
|
||||
drivers.emplace_back(std::move(driver));
|
||||
++itr;
|
||||
} else {
|
||||
itr = drivers_.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
// event handling
|
||||
const auto events = events_->Take();
|
||||
for (const auto& event : events) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
}
|
||||
|
||||
// frame reset
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// draw something
|
||||
for (const auto& driver : drivers) { driver->Update(t); }
|
||||
ImGui::Render();
|
||||
|
||||
// render them
|
||||
const auto& io = ImGui::GetIO();
|
||||
glViewport(0, 0,
|
||||
static_cast<int>(io.DisplaySize.x),
|
||||
static_cast<int>(io.DisplaySize.y));
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
for (const auto& driver : drivers) { driver->PreUpdate(t); }
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
for (const auto& driver : drivers) { driver->PostUpdate(t); }
|
||||
SDL_GL_SwapWindow(t.win());
|
||||
|
||||
// schedule next frame
|
||||
gl3_->Push(gl3::Task {
|
||||
clock_->now() + kUpdateInterval,
|
||||
[wself = weak_from_this()](auto& t) {
|
||||
if (auto self = wself.lock()) { self->Update(t); }
|
||||
},
|
||||
});
|
||||
}
|
||||
void TearDown(gl3::TaskContext&) noexcept {
|
||||
ImGui::SetCurrentContext(imgui_);
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext(imgui_);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<subsys::Clock> clock_;
|
||||
const std::shared_ptr<gl3::Context> gl3_;
|
||||
const std::unique_ptr<EventQueue> events_;
|
||||
|
||||
ImGuiContext* const imgui_;
|
||||
|
||||
std::vector<std::weak_ptr<Driver>> drivers_;
|
||||
};
|
||||
|
||||
Context::Context(Env& env)
|
||||
try : subsys::Interface("nf7::core::imgui::Context"),
|
||||
impl_(std::make_shared<Impl>(env)) {
|
||||
impl_->ScheduleStart();
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw MemoryException {};
|
||||
}
|
||||
Context::~Context() noexcept {
|
||||
impl_->ScheduleTearDown();
|
||||
}
|
||||
const std::shared_ptr<Driver>& Context::Register(
|
||||
const std::shared_ptr<Driver>& driver) {
|
||||
return impl_->Register(driver);
|
||||
}
|
||||
|
||||
} // namespace nf7::core::imgui
|
31
core/imgui/context.hh
Normal file
31
core/imgui/context.hh
Normal file
@ -0,0 +1,31 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "iface/env.hh"
|
||||
|
||||
#include "core/imgui/driver.hh"
|
||||
|
||||
|
||||
namespace nf7::core::imgui {
|
||||
|
||||
class Context : public subsys::Interface {
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
public:
|
||||
explicit Context(Env&);
|
||||
~Context() noexcept override;
|
||||
|
||||
public:
|
||||
const std::shared_ptr<Driver>& Register(
|
||||
const std::shared_ptr<Driver>& driver);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace nf7::core::imgui
|
38
core/imgui/context_test.cc
Normal file
38
core/imgui/context_test.cc
Normal file
@ -0,0 +1,38 @@
|
||||
// No copyright
|
||||
#include "core/imgui/context.hh"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "core/imgui/context_test.hh"
|
||||
#include "core/imgui/driver_test.hh"
|
||||
|
||||
|
||||
// adds meaningless suffix to avoid a conflict with ImGuiContext
|
||||
using ImGuiContext0 = nf7::core::imgui::test::ContextFixture;
|
||||
|
||||
TEST_F(ImGuiContext0, Init) {
|
||||
const auto clock = env().Get<nf7::subsys::Clock>();
|
||||
const auto concurrency = env().Get<nf7::subsys::Concurrency>();
|
||||
|
||||
auto ctx = env().Get<nf7::core::imgui::Context>();
|
||||
concurrency->Push(nf7::SyncTask {
|
||||
clock->now() + std::chrono::seconds {10},
|
||||
[&](auto&) { ctx = nullptr; },
|
||||
});
|
||||
|
||||
auto driver = std::make_shared<
|
||||
::testing::NiceMock<nf7::core::imgui::test::DriverMock>>();
|
||||
ctx->Register(driver);
|
||||
|
||||
bool shown = true;
|
||||
ON_CALL(*driver, Update).WillByDefault([&](auto&) {
|
||||
ImGui::ShowDemoWindow(&shown);
|
||||
});
|
||||
|
||||
DropEnv();
|
||||
ConsumeTasks();
|
||||
}
|
16
core/imgui/context_test.hh
Normal file
16
core/imgui/context_test.hh
Normal file
@ -0,0 +1,16 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/gl3/context_test.hh"
|
||||
|
||||
|
||||
namespace nf7::core::imgui::test {
|
||||
|
||||
class ContextFixture : public gl3::test::ContextFixture {
|
||||
public:
|
||||
ContextFixture() { Install<Context, Context>(); }
|
||||
};
|
||||
|
||||
} // namespace nf7::core::imgui::test
|
25
core/imgui/driver.hh
Normal file
25
core/imgui/driver.hh
Normal file
@ -0,0 +1,25 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include "core/gl3/context.hh"
|
||||
|
||||
|
||||
namespace nf7::core::imgui {
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
Driver() = default;
|
||||
virtual ~Driver() = default;
|
||||
|
||||
Driver(const Driver&) = delete;
|
||||
Driver(Driver&&) = delete;
|
||||
Driver& operator=(const Driver&) = delete;
|
||||
Driver& operator=(Driver&&) = delete;
|
||||
|
||||
public:
|
||||
virtual void PreUpdate(gl3::TaskContext&) noexcept { }
|
||||
virtual void Update(gl3::TaskContext&) noexcept { }
|
||||
virtual void PostUpdate(gl3::TaskContext&) noexcept { }
|
||||
};
|
||||
|
||||
} // namespace nf7::core::imgui
|
22
core/imgui/driver_test.hh
Normal file
22
core/imgui/driver_test.hh
Normal file
@ -0,0 +1,22 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include "core/imgui/driver.hh"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "core/gl3/context.hh"
|
||||
|
||||
|
||||
namespace nf7::core::imgui::test {
|
||||
|
||||
class DriverMock : public Driver {
|
||||
public:
|
||||
using Driver::Driver;
|
||||
|
||||
MOCK_METHOD(void, PreUpdate, (gl3::TaskContext&), (noexcept, override));
|
||||
MOCK_METHOD(void, Update, (gl3::TaskContext&), (noexcept, override));
|
||||
MOCK_METHOD(void, PostUpdate, (gl3::TaskContext&), (noexcept, override));
|
||||
};
|
||||
|
||||
} // namespace nf7::core::imgui::test
|
@ -159,7 +159,7 @@ class AsyncContext final :
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<Context> Context::Create(Env& env, Kind kind) {
|
||||
std::shared_ptr<Context> Context::Make(Env& env, Kind kind) {
|
||||
switch (kind) {
|
||||
case kSync:
|
||||
return std::make_shared<SyncContext>(env);
|
||||
|
@ -147,7 +147,13 @@ class Context :
|
||||
kSync,
|
||||
kAsync,
|
||||
};
|
||||
static std::shared_ptr<Context> Create(Env&, Kind);
|
||||
static std::shared_ptr<Context> Make(Env&, Kind);
|
||||
static std::shared_ptr<Context> MakeAsync(Env& env) {
|
||||
return Make(env, kAsync);
|
||||
}
|
||||
static std::shared_ptr<Context> MakeSync(Env& env) {
|
||||
return Make(env, kSync);
|
||||
}
|
||||
|
||||
explicit Context(const char* name, Kind kind)
|
||||
: subsys::Interface(name), kind_(kind), state_(nullptr) {
|
||||
|
@ -26,14 +26,9 @@ class ContextFixture :
|
||||
public nf7::core::test::EnvFixtureWithTasking,
|
||||
public ::testing::WithParamInterface<Context::Kind> {
|
||||
public:
|
||||
ContextFixture() noexcept
|
||||
: EnvFixtureWithTasking({
|
||||
{
|
||||
typeid(Context), [](auto& env) {
|
||||
return Context::Create(env, GetParam());
|
||||
},
|
||||
},
|
||||
}) { }
|
||||
ContextFixture() {
|
||||
Install<Context>([](auto& env) { return Context::Make(env, GetParam()); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nf7::core::luajit::test
|
||||
|
@ -15,12 +15,9 @@ namespace nf7::core::sqlite::test {
|
||||
|
||||
class DatabaseFixture : public nf7::core::test::EnvFixtureWithTasking {
|
||||
public:
|
||||
DatabaseFixture()
|
||||
: nf7::core::test::EnvFixtureWithTasking({
|
||||
{typeid(nf7::subsys::Database), [](auto& env) {
|
||||
return std::make_shared<Database>(env, ":memory:");
|
||||
}},
|
||||
}) {
|
||||
DatabaseFixture() {
|
||||
Install<subsys::Database>(
|
||||
[](auto& env) { return std::make_shared<Database>(env, ":memory:"); });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,11 +20,9 @@ namespace nf7::core::uv::test {
|
||||
|
||||
class ContextFixture : public nf7::core::test::EnvFixture {
|
||||
public:
|
||||
ContextFixture() noexcept
|
||||
: nf7::core::test::EnvFixture({
|
||||
SimpleEnv::MakePair<Context, MainContext>(),
|
||||
SimpleEnv::MakePair<subsys::Clock, Clock>(),
|
||||
}) {
|
||||
ContextFixture() {
|
||||
Install<Context, MainContext>();
|
||||
Install<subsys::Clock, Clock>();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
4
thirdparty/CMakeLists.txt
vendored
4
thirdparty/CMakeLists.txt
vendored
@ -29,6 +29,10 @@ FetchContent_Declare(
|
||||
FetchContent_Populate(luajit)
|
||||
include(luajit.cmake)
|
||||
|
||||
# ---- OpenGL
|
||||
find_package(OpenGL REQUIRED GLOBAL)
|
||||
add_library(OpenGL ALIAS OpenGL::GL)
|
||||
|
||||
# ---- SDL2 (zlib)
|
||||
find_package(SDL2 REQUIRED GLOBAL)
|
||||
add_library(SDL2 ALIAS SDL2::SDL2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user