Compare commits

...

2 Commits

Author SHA1 Message Date
0e76f9ccb2 add imgui::Context 2023-09-23 19:35:49 +09:00
d8cd074f40 add new thirdparty lib, opengl 2023-09-23 19:24:45 +09:00
7 changed files with 288 additions and 0 deletions

View File

@ -6,6 +6,7 @@ target_link_libraries(nf7_core
luajit luajit
nf7_config nf7_config
nf7_iface nf7_iface
OpenGL
SDL2 SDL2
sqlite sqlite
uvw uvw
@ -13,6 +14,7 @@ target_link_libraries(nf7_core
target_sources(nf7_core target_sources(nf7_core
PRIVATE PRIVATE
gl3/context.cc gl3/context.cc
imgui/context.cc
luajit/context.cc luajit/context.cc
luajit/lambda.cc luajit/lambda.cc
luajit/thread.cc luajit/thread.cc
@ -22,6 +24,8 @@ target_sources(nf7_core
version.cc version.cc
PUBLIC PUBLIC
gl3/context.hh gl3/context.hh
imgui/context.hh
imgui/driver.hh
luajit/context.hh luajit/context.hh
luajit/lambda.hh luajit/lambda.hh
luajit/thread.hh luajit/thread.hh
@ -44,6 +48,7 @@ add_executable(nf7_core_test)
target_sources(nf7_core_test target_sources(nf7_core_test
PRIVATE PRIVATE
gl3/context_test.cc gl3/context_test.cc
imgui/context_test.cc
luajit/context_test.cc luajit/context_test.cc
luajit/context_test.hh luajit/context_test.hh
luajit/lambda_test.cc luajit/lambda_test.cc

163
core/imgui/context.cc Normal file
View 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
View 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

View 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/gl3/context_test.hh"
#include "core/imgui/driver_test.hh"
// adds meaningless suffix to avoid a conflict with ImGuiContext
using ImGuiContext0 = nf7::core::gl3::test::ContextFixture;
TEST_F(ImGuiContext0, Init) {
const auto clock = env().Get<nf7::subsys::Clock>();
const auto concurrency = env().Get<nf7::subsys::Concurrency>();
auto ctx = std::make_shared<nf7::core::imgui::Context>(env());
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();
}

25
core/imgui/driver.hh Normal file
View 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
View 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

View File

@ -29,6 +29,10 @@ FetchContent_Declare(
FetchContent_Populate(luajit) FetchContent_Populate(luajit)
include(luajit.cmake) include(luajit.cmake)
# ---- OpenGL
find_package(OpenGL REQUIRED GLOBAL)
add_library(OpenGL ALIAS OpenGL::GL)
# ---- SDL2 (zlib) # ---- SDL2 (zlib)
find_package(SDL2 REQUIRED GLOBAL) find_package(SDL2 REQUIRED GLOBAL)
add_library(SDL2 ALIAS SDL2::SDL2) add_library(SDL2 ALIAS SDL2::SDL2)