diff --git a/conanfile.py b/conanfile.py index b85011cf..4deaf911 100644 --- a/conanfile.py +++ b/conanfile.py @@ -79,6 +79,7 @@ class Morpheus(ConanFile): "unordered_dense/4.5.0", "boost/1.88.0", "ctre/3.9.0", + "ftxui/6.0.2", "magic_enum/0.9.7", "ms-gsl/4.1.0", "rapidjson/cci.20230929", diff --git a/examples/gfx/render_triangle/CMakeLists.txt b/examples/gfx/render_triangle/CMakeLists.txt index 629ba15d..d18b109d 100644 --- a/examples/gfx/render_triangle/CMakeLists.txt +++ b/examples/gfx/render_triangle/CMakeLists.txt @@ -1,10 +1,13 @@ add_executable(RenderTriangle main.cpp) +find_package(ftxui) target_link_libraries(RenderTriangle PRIVATE + ftxui::ftxui morpheus::application morpheus::gfx::platform morpheus::gfx::gl4 + morpheus::vis ) set_target_properties(RenderTriangle diff --git a/examples/gfx/render_triangle/main.cpp b/examples/gfx/render_triangle/main.cpp index 1b311a9d..d62eb9ab 100644 --- a/examples/gfx/render_triangle/main.cpp +++ b/examples/gfx/render_triangle/main.cpp @@ -1,8 +1,11 @@ #include #include #include -// #include #include +#include + +#include +#include using namespace morpheus::application; using namespace morpheus::gfx; @@ -45,13 +48,158 @@ class RenderTriange : public Application int main(int argc, char* argv[]) { + // const std::vector renders = { + // "Direct X 12", + // "OpenGL 4", + // "Vulkan" + // }; - tryCatch( - [&] - { - RenderTriange example(argc, argv); - example.Run(); - }); + // int selectedRenders = 0; + // ftxui::Component compiler = ftxui::Radiobox(&renders, &selectedRenders); + + // auto screen = ftxui::ScreenInteractive::Fullscreen(); + ////auto testComponent = ftxui::Renderer([]() { return ftxui::text("test Component"); }); + // auto testComponent = ftxui::Renderer(compiler, [&]() { + // auto rendererWin = ftxui::window(ftxui::text("Renderer"), compiler->Render() | ftxui::vscroll_indicator | ftxui::frame); + // return rendererWin; + // }); + // screen.Loop(testComponent); + + morpheus::vis::RenderSystemFactory factory; + factory.runTuiConfiguration(); + + // tryCatch( + // [&] + // { + // RenderTriange example(argc, argv); + // example.Run(); + // }); // render_system_factory renderer_factory; } + +/* +#include +#include +#include +#include +#include +#include +#include + +using namespace ftxui; + +// Enum to track the current screen state +enum class ScreenStage { + SelectRenderer, + ConfigureRenderer, +}; + +int main() { + ScreenInteractive screen = ScreenInteractive::TerminalOutput(); + + // State + ScreenStage current_stage = ScreenStage::SelectRenderer; + + std::vector renderer_options = { + "DirectX 12", "Vulkan", "OpenGL" + }; + int selected_renderer = 0; + auto renderer_selector = Radiobox(&renderer_options, &selected_renderer); + + // Settings per renderer + std::map> renderer_settings = { + {"DirectX 12", {"Use DXIL", "Enable Tearing"}}, + {"Vulkan", {"Validation Layers", "Prefer Integrated GPU"}}, + {"OpenGL", {"Core Profile", "Enable Debug Output"}} + }; + + // Dynamic state for second screen + std::vector setting_labels; + std::vector setting_states; // Real bools for safety + std::vector setting_checkboxes; + Component settings_container = Container::Vertical({}); + + // Component placeholders + Component next_button, back_button, finish_button; + + // Container that will switch content + Component main_container = Container::Vertical({}); + + // Renderer root + Component root = Renderer(main_container, [&] { + if (current_stage == ScreenStage::SelectRenderer) { + return vbox({ + text("Select Render System:") | bold, + renderer_selector->Render(), + separator(), + next_button->Render(), + }) | border | center; + } + else { + Elements checkbox_elements; + for (auto& cb : setting_checkboxes) { + checkbox_elements.push_back(cb->Render()); + } + + return vbox({ + text("Configure Settings for: ") | bold, + text(renderer_options[selected_renderer]), + separator(), + vbox(std::move(checkbox_elements)), + separator(), + hbox({ + back_button->Render(), + finish_button->Render(), + }) | center + }) | border | center; + } + }); + + // Buttons — defined after root to capture state + next_button = Button("Next", [&] { + current_stage = ScreenStage::ConfigureRenderer; + + // Populate setting labels + bools + std::string selected_name = renderer_options[selected_renderer]; + setting_labels = renderer_settings[selected_name]; + setting_states = std::vector(setting_labels.size(), false); + setting_checkboxes.clear(); + + for (size_t i = 0; i < setting_labels.size(); ++i) { + bool local = setting_states[i]; + setting_checkboxes.push_back(Checkbox(setting_labels[i], &local)); + } + + settings_container = Container::Vertical(setting_checkboxes); + + // Reconfigure main container + main_container->DetachAllChildren(); + //main_container->Add(renderer_selector); + main_container->Add(settings_container); + main_container->Add(back_button); + main_container->Add(finish_button); + + screen.PostEvent(Event::Custom); // force re-render + }); + + back_button = Button("Back", [&] { + current_stage = ScreenStage::SelectRenderer; + main_container->DetachAllChildren(); + main_container->Add(renderer_selector); + main_container->Add(next_button); + screen.PostEvent(Event::Custom); // force re-render + }); + + finish_button = Button("Finish", [&] { + screen.Exit(); + }); + + // Initial container setup + main_container->Add(renderer_selector); + main_container->Add(next_button); + + screen.Loop(root); + return 0; +} +*/ diff --git a/libraries/core/lib/morpheus/core/base/platform.hpp b/libraries/core/lib/morpheus/core/base/platform.hpp index 23ffcb56..f9260412 100644 --- a/libraries/core/lib/morpheus/core/base/platform.hpp +++ b/libraries/core/lib/morpheus/core/base/platform.hpp @@ -1,9 +1,10 @@ #pragma once -#include #include #include +#include + /*! \defgroup Platform Morpheus Supported Platforms The platform group of macros allow for compile time detection of the current platform. @{ diff --git a/libraries/core/lib/morpheus/core/functional/function_ref.hpp b/libraries/core/lib/morpheus/core/functional/function_ref.hpp index 287a1495..cc335df6 100644 --- a/libraries/core/lib/morpheus/core/functional/function_ref.hpp +++ b/libraries/core/lib/morpheus/core/functional/function_ref.hpp @@ -1,9 +1,10 @@ #pragma once -#include -#include #include #include + +#include +#include #include #include @@ -11,12 +12,12 @@ namespace morpheus::conf { -namespace func_ref = std; +namespace fr = std; } #else -namespace morpheus::conf::func_ref +namespace morpheus::conf::fr { /// \struct nontype_t @@ -98,7 +99,8 @@ class function_ref template function_ref(F* f) noexcept requires std::is_function_v and isInvokableWith - : mInvoke([](Storage storage, Args... args) noexcept(isNoexcept) { return invoke(function_ref::get(storage), std::forward(args)...); }) + : mInvoke([](Storage storage, Args... args) noexcept(isNoexcept) -> Return + { return invoke(function_ref::get(storage), std::forward(args)...); }) , mStorage(f) {} @@ -180,7 +182,7 @@ class function_ref template requires std::is_function_v constexpr explicit Storage(F* f) noexcept - : fp(f) + : fp(reinterpret_cast(f)) {} }; @@ -193,7 +195,7 @@ class function_ref else if constexpr (std::is_object_v) return static_cast(storage.p); else - return static_cast(storage.fp); + return reinterpret_cast(storage.fp); } using InternalInvocableType = Return(Storage, Args...); @@ -226,5 +228,6 @@ function_ref(nontype_t) -> function_ref>; function_ref(F) -> function_ref; */ -} // namespace morpheus::conf::func_ref +} // namespace morpheus::conf::fr + #endif diff --git a/libraries/core/tests/functional/function_ref.tests.cpp b/libraries/core/tests/functional/function_ref.tests.cpp index 9e356bf1..2204b519 100644 --- a/libraries/core/tests/functional/function_ref.tests.cpp +++ b/libraries/core/tests/functional/function_ref.tests.cpp @@ -9,7 +9,7 @@ namespace morpheus::functional TEST_CASE("Propagate constness and noexceptness to function_ref", "[morpheus.functional.function_ref]") { - using ConcreteFunctionRef = conf::func_ref::function_ref; + using ConcreteFunctionRef = conf::fr::function_ref; STATIC_REQUIRE(std::is_nothrow_copy_constructible_v); STATIC_REQUIRE(std::is_nothrow_copy_assignable_v); STATIC_REQUIRE(std::is_nothrow_move_constructible_v); @@ -27,7 +27,7 @@ TEST_CASE("Verify construction of function_ref", "[morpheus.functional.function_ { WHEN("Constructing a function reference to the function") { - conf::func_ref::function_ref functionView = testFunction; + conf::fr::function_ref functionView = testFunction; THEN("Expect the function to be invocable by the function ref") { functionView(); @@ -45,7 +45,7 @@ TEST_CASE("Verify construction of function_ref", "[morpheus.functional.function_ WHEN("Constructing a function reference to the function") { TestForInvocable instance; - conf::func_ref::function_ref functionView = {conf::func_ref::nontype<&TestForInvocable::function>, instance}; + conf::fr::function_ref functionView = {conf::fr::nontype<&TestForInvocable::function>, instance}; THEN("Expect the function to be invocable by the function ref") { functionView(0, 1); diff --git a/libraries/gfx/gl4/tests/wgl/adapter.tests.cpp b/libraries/gfx/gl4/tests/wgl/adapter.tests.cpp index db348f29..0344dad3 100644 --- a/libraries/gfx/gl4/tests/wgl/adapter.tests.cpp +++ b/libraries/gfx/gl4/tests/wgl/adapter.tests.cpp @@ -1,9 +1,12 @@ #include #include +#include #include #include + +#include #include // #include @@ -33,43 +36,129 @@ */ #ifdef _WIN32 #include -extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; -extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +// extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000000; +extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000000; #endif namespace morpheus::gfx::gl4::wgl { +namespace +{ using PathInfoArray = std::vector; using ModeInfoArray = std::vector; using DisplayConfig = std::pair; -conf::exp::expected getCurrentDisplayConfig() +/// Path mapping an output monitor to the graphic adapter mapped to it. +using Path = std::pair; + +auto getDisplayConfigBufferSizes() -> conf::exp::expected, std::string> +{ + UINT32 pathCount = 0, modeCount = 0; + auto const result = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount); + if (result != ERROR_SUCCESS) + { + return conf::exp::unexpected(win32::GetLastErrorString(result)); + } + return { + std::pair{pathCount, modeCount} + }; +} + +auto queryDisplayConfig(DisplayConfig config) -> conf::exp::expected { - PathInfoArray pathInfo; - ModeInfoArray modeInfo; + auto& [pathInfo, modeInfo] = config; + UINT32 pathSize = static_cast(pathInfo.size()); + UINT32 modeSize = static_cast(modeInfo.size()); + ULONG const result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathSize, pathInfo.data(), &modeSize, modeInfo.data(), NULL); + if (result != ERROR_SUCCESS) + return conf::exp::unexpected(win32::GetLastErrorString(result)); + return config; +} - // First, grab the system's current configuration - for (UINT32 trySize = 32; trySize < 1024; trySize *= 2) +auto targetDeviceName(DISPLAYCONFIG_PATH_INFO const& path) -> conf::exp::expected +{ + DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {}; + targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + targetName.header.size = sizeof(targetName); + targetName.header.adapterId = path.targetInfo.adapterId; + targetName.header.id = path.targetInfo.id; + auto const result = DisplayConfigGetDeviceInfo(&targetName.header); + if (result != ERROR_SUCCESS) { - pathInfo.resize(trySize); - modeInfo.resize(trySize); - UINT32 pathSize = static_cast(pathInfo.size()); - UINT32 modeSize = static_cast(modeInfo.size()); + return conf::exp::unexpected(win32::GetLastErrorString(result)); + } + return targetName; +} + +auto adapterName(DISPLAYCONFIG_PATH_INFO const& path) -> conf::exp::expected +{ + DISPLAYCONFIG_ADAPTER_NAME adapterName = {}; + adapterName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME; + adapterName.header.size = sizeof(adapterName); + adapterName.header.adapterId = path.sourceInfo.adapterId; + adapterName.header.id = 0; + auto const result = DisplayConfigGetDeviceInfo(&adapterName.header); + if (result != ERROR_SUCCESS) + { + return conf::exp::unexpected(win32::GetLastErrorString(result)); + } + return adapterName; +} + +} // namespace + +conf::exp::expected getCurrentDisplayConfig() +{ + // clang-format off + return getDisplayConfigBufferSizes() + .and_then([](auto counts) -> conf::exp::expected + { + return std::apply([](auto&&... args) + { + return DisplayConfig(std::forward(args)...); + }, + std::move(counts) + ); + }) + .and_then([](auto config) + { return queryDisplayConfig(std::move(config)); }); + // clang-format on +} - ULONG const rc = QueryDisplayConfig(QDC_ALL_PATHS, &pathSize, pathInfo.data(), &modeSize, modeInfo.data(), NULL); +auto getDevicesPaths(DisplayConfig const& config) -> conf::exp::expected, std::string> +{ + auto& [paths, _] = config; - if (rc == ERROR_SUCCESS) + // clang-format off + auto r = paths | std::ranges::views::transform([](auto const& path) { - pathInfo.resize(pathSize); - modeInfo.resize(modeSize); - break; - } + return std::pair{targetDeviceName(path), adapterName(path)}; + }); - if (rc != ERROR_INSUFFICIENT_BUFFER) - return conf::exp::unexpected(rc); - } - return DisplayConfig{std::move(pathInfo), std::move(modeInfo)}; + auto const result = std::ranges::fold_left(std::move(r), conf::exp::expected, std::string>{}, + [](auto&& result, auto element) -> conf::exp::expected, std::string> + { + if (!result) { + return conf::exp::unexpected(result.error()); + } + + if (!element.first) { + return conf::exp::unexpected(element.first.error()); + } + + if (!element.second) { + return conf::exp::unexpected(element.second.error()); + } + + result.value().push_back(Path{std::move(element).first.value(), std::move(element).second.value()}); + return std::move(result); + }); + // clang-format on + + return result; } TEST_CASE("Create an adapter mode list", "[morpheus.core.gfx.gl.wgl.adapter_list]") @@ -112,10 +201,12 @@ TEST_CASE("Create an adapter mode list", "[morpheus.core.gfx.gl.wgl.adapter_list */ DISPLAYCONFIG_PATH_SOURCE_INFO* pSource = NULL; // will contain the answer - auto result = getCurrentDisplayConfig(); - REQUIRE(result); + auto result = getCurrentDisplayConfig().and_then([](DisplayConfig const& config) { return getDevicesPaths(config); }); + + auto result4 = getCurrentDisplayConfig(); + REQUIRE(result4); - auto& [pathInfo, modeInfo] = result.value(); + auto& [pathInfo, modeInfo] = result4.value(); for (UINT32 tryEnable = 0;; ++tryEnable) { diff --git a/libraries/gfx/platform/apps/detect_monitors/main.cpp b/libraries/gfx/platform/apps/detect_monitors/main.cpp index 4ffc3c0d..a40d7c84 100644 --- a/libraries/gfx/platform/apps/detect_monitors/main.cpp +++ b/libraries/gfx/platform/apps/detect_monitors/main.cpp @@ -6,7 +6,7 @@ using namespace morpheus; using namespace morpheus::application::po; // LCOV_EXCL_START -int main(int argc, char* argv[]) +int main(int /*argc*/, char*[] /*argv[]*/) { for (auto const& monitor : morpheus::gfx::os::enumerateMonitors()) { diff --git a/libraries/gfx/platform/lib/morpheus/gfx/platform/concepts/render_system.hpp b/libraries/gfx/platform/lib/morpheus/gfx/platform/concepts/render_system.hpp index 0238995c..0e5c6966 100644 --- a/libraries/gfx/platform/lib/morpheus/gfx/platform/concepts/render_system.hpp +++ b/libraries/gfx/platform/lib/morpheus/gfx/platform/concepts/render_system.hpp @@ -17,9 +17,10 @@ template concept RenderSystem = requires(T t) { // typename T::Window; + /// requires !meta::Copyable; + { T::getGraphicsApi() } -> std::same_as; - // requires !meta::Copyable; // { t.adapters() } -> AdapterRange; // { t.beginFrame() } -> std::same_as; diff --git a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/CMakeLists.txt b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/CMakeLists.txt index 76add655..51e616e6 100644 --- a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/CMakeLists.txt +++ b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/CMakeLists.txt @@ -4,11 +4,13 @@ target_sources(MorpheusGfxPlatform FILES error_codes.hpp error.hpp + error_codes.hpp monitor.hpp render_window.hpp PRIVATE error_codes.cpp error.cpp + error_codes.cpp monitor.cpp render_window.cpp ) diff --git a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.cpp b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.cpp index 1a6929e3..cadc0113 100644 --- a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.cpp +++ b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.cpp @@ -1,52 +1,52 @@ -#include -#include - -#include - -namespace morpheus::gfx::win32 -{ - -namespace details -{ -/*! \class vulkan_error_category - Defines an error code category for Vulkan return codes to plug them into the std::error_code framework. - */ -class ErrorCategory : public std::error_category -{ -public: - /*! Queries a short form descriptive name for the category. - \return Short form name of the category. - */ - char const* name() const noexcept override final { return "Win32::ErrorCategory"; } - - /*! Queries an error string describing the error. - \return The error message. - */ - std::string message(int c) const override final { return GetLastErrorString(static_cast(c)); } - - static ErrorCategory const& getSingleInstance() noexcept { return mSingleInstance; } - -private: - static ErrorCategory mSingleInstance; -}; - -ErrorCategory ErrorCategory::mSingleInstance; - -} // namespace details - -details::ErrorCategory const& ErrorCategory() noexcept -{ - return details::ErrorCategory::getSingleInstance(); -} - -} // namespace morpheus::gfx::win32 - -std::error_code make_error_code(morpheus::gfx::win32::ErrorCode const& e) -{ - return std::error_code(static_cast(e.error), morpheus::gfx::win32::ErrorCategory()); -} - -std::error_code getLastErrorCode() -{ - return make_error_code(morpheus::gfx::win32::ErrorCode{::GetLastError()}); -} +#include +#include + +#include + +namespace morpheus::gfx::win32 +{ + +namespace details +{ +/*! \class vulkan_error_category + Defines an error code category for Vulkan return codes to plug them into the std::error_code framework. + */ +class ErrorCategory : public std::error_category +{ +public: + /*! Queries a short form descriptive name for the category. + \return Short form name of the category. + */ + char const* name() const noexcept override final { return "Win32::ErrorCategory"; } + + /*! Queries an error string describing the error. + \return The error message. + */ + std::string message(int c) const override final { return GetLastErrorString(static_cast(c)); } + + static ErrorCategory const& getSingleInstance() noexcept { return mSingleInstance; } + +private: + static ErrorCategory mSingleInstance; +}; + +ErrorCategory ErrorCategory::mSingleInstance; + +} // namespace details + +details::ErrorCategory const& ErrorCategory() noexcept +{ + return details::ErrorCategory::getSingleInstance(); +} + +} // namespace morpheus::gfx::win32 + +std::error_code make_error_code(morpheus::gfx::win32::ErrorCode const& e) +{ + return std::error_code(static_cast(e.error), morpheus::gfx::win32::ErrorCategory()); +} + +std::error_code getLastErrorCode() +{ + return make_error_code(morpheus::gfx::win32::ErrorCode{::GetLastError()}); +} diff --git a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.hpp b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.hpp index a9c8a10d..e45d012c 100644 --- a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.hpp +++ b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/error_codes.hpp @@ -1,54 +1,54 @@ -#pragma once - -#include - -#include - -namespace morpheus::gfx::win32 -{ - -struct [[nodiscard]] ErrorCode -{ - constexpr explicit ErrorCode(DWORD const e) noexcept - : error(e) - {} - DWORD error; -}; - -} // namespace morpheus::gfx::win32 - -namespace std -{ -template <> -struct is_error_code_enum : true_type -{}; -} // namespace std - -namespace morpheus::gfx::win32 -{ - -namespace details -{ -class ErrorCategory; -} - -/// Retrieve the one instance of the Win32 error category -/// \return The one instance of the Win32 error category. -details::ErrorCategory const& ErrorCategory() noexcept; - -} // namespace morpheus::gfx::win32 - -/// Overload the global make_error_code() free function with a ErrorCode. It will be found via ADL by the compiler if needed. -/// \param[in] error The error raised by the underlying Win32 API. -/// \return A std::error_code with Win32 error encoded into it. -/// \note Because windows error codes are just DWORD values, and thus not strongly typed, you must explicitly wrap them in an ErrorCode constructed from a -/// DWORD to pass to the make_error_code overload or call the helper overload getLastErrorCode() which initialised an error code from GetLastError(). -/// \code -/// std::error_code ec = make_error_code(morpheus::gfx::win32::ErrorCode{ERROR_FILE_NOT_FOUND}); -/// \endcode -std::error_code make_error_code(morpheus::gfx::win32::ErrorCode const& e); - -/// Overload the getLastErrorCode which is initialised from GetLastError(). -/// \param[in] error The error raised by the underlying Win32 API. -/// \return A std::error_code with Win32 error encoded into it. -std::error_code getLastErrorCode(); +#pragma once + +#include + +#include + +namespace morpheus::gfx::win32 +{ + +struct [[nodiscard]] ErrorCode +{ + constexpr explicit ErrorCode(DWORD const e) noexcept + : error(e) + {} + DWORD error; +}; + +} // namespace morpheus::gfx::win32 + +namespace std +{ +template <> +struct is_error_code_enum : true_type +{}; +} // namespace std + +namespace morpheus::gfx::win32 +{ + +namespace details +{ +class ErrorCategory; +} + +/// Retrieve the one instance of the Win32 error category +/// \return The one instance of the Win32 error category. +details::ErrorCategory const& ErrorCategory() noexcept; + +} // namespace morpheus::gfx::win32 + +/// Overload the global make_error_code() free function with a ErrorCode. It will be found via ADL by the compiler if needed. +/// \param[in] error The error raised by the underlying Win32 API. +/// \return A std::error_code with Win32 error encoded into it. +/// \note Because windows error codes are just DWORD values, and thus not strongly typed, you must explicitly wrap them in an ErrorCode constructed from a +/// DWORD to pass to the make_error_code overload or call the helper overload getLastErrorCode() which initialised an error code from GetLastError(). +/// \code +/// std::error_code ec = make_error_code(morpheus::gfx::win32::ErrorCode{ERROR_FILE_NOT_FOUND}); +/// \endcode +std::error_code make_error_code(morpheus::gfx::win32::ErrorCode const& e); + +/// Overload the getLastErrorCode which is initialised from GetLastError(). +/// \param[in] error The error raised by the underlying Win32 API. +/// \return A std::error_code with Win32 error encoded into it. +std::error_code getLastErrorCode(); diff --git a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/render_window.cpp b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/render_window.cpp index 6e486b85..f9edda30 100644 --- a/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/render_window.cpp +++ b/libraries/gfx/platform/lib/morpheus/gfx/platform/win32/render_window.cpp @@ -398,6 +398,7 @@ bool RenderWindow::visible() const noexcept // If only it where this simple, IsWindowVisible only queries the status flag, not if it is actually visible: https://stackoverflow.com/a/6269768/4764531 return IsWindowVisible(mWindow.get()) == TRUE; } + void RenderWindow::resize() { if (!visible()) diff --git a/libraries/gfx/vulkan/lib/morpheus/gfx/vulkan/render_system.cpp b/libraries/gfx/vulkan/lib/morpheus/gfx/vulkan/render_system.cpp index 015056f7..ec5be040 100644 --- a/libraries/gfx/vulkan/lib/morpheus/gfx/vulkan/render_system.cpp +++ b/libraries/gfx/vulkan/lib/morpheus/gfx/vulkan/render_system.cpp @@ -36,9 +36,8 @@ RenderSystem::RenderSystem(std::string_view const appName, std::string_view cons , mAvailableLayers(mContext.enumerateInstanceLayerProperties()) , mAdapters(enumerateAdapters(mInstance)) {} -// LCOV_EXCL_STOP -//--------------------------------------------------------------------------------------------------------------------- +// LCOV_EXCL_STOP //--------------------------------------------------------------------------------------------------------------------- } // namespace morpheus::gfx::vulkan diff --git a/libraries/visualisation/lib/morpheus/vis/CMakeLists.txt b/libraries/visualisation/lib/morpheus/vis/CMakeLists.txt index e0dd95d9..9a241648 100644 --- a/libraries/visualisation/lib/morpheus/vis/CMakeLists.txt +++ b/libraries/visualisation/lib/morpheus/vis/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(ftxui) + target_sources(MorpheusVisualisation PUBLIC FILE_SET HEADERS @@ -8,3 +10,8 @@ target_sources(MorpheusVisualisation render_system_factory.cpp visualisation.cpp ) + +target_link_libraries(MorpheusVisualisation + PRIVATE + ftxui::ftxui +) diff --git a/libraries/visualisation/lib/morpheus/vis/render_system_factory.cpp b/libraries/visualisation/lib/morpheus/vis/render_system_factory.cpp index 322b3fa4..3524ddef 100644 --- a/libraries/visualisation/lib/morpheus/vis/render_system_factory.cpp +++ b/libraries/visualisation/lib/morpheus/vis/render_system_factory.cpp @@ -1,10 +1,87 @@ +#include #include +#include + +#include +#include + +namespace hana = boost::hana; + namespace morpheus::vis { -//--------------------------------------------------------------------------------------------------------------------- +void RenderSystemFactory::addOptions(boost::program_options::options_description& options) +{ + namespace po = boost::program_options; + options.add_options()("render-system", po::value(&mActiveApi)->default_value(mActiveApi), "The rendering system to instantiate."); +} + +// boost::program_options::options_description& RenderSystemFactory::runTuiConfiguration() + +// LCOV_EXCL_START +void RenderSystemFactory::runTuiConfiguration() +{ + auto const renderSystems = []() + { + std::vector results; + boost::hana::for_each(availableAPIs(), + [&](auto const& pair) + { + // constexpr auto key = hana::first(pair); + auto value = hana::second(pair); + using RenderSystem = typename decltype(value)::type; + results.push_back(std::string(RenderSystem::getGraphicsApi())); + }); + return results; + }(); + + using namespace ftxui; + + enum class Pages + { + SelectRenderSystem, + RenderSystemSettings, + }; + + Pages current = Pages::SelectRenderSystem; + auto screen = ftxui::ScreenInteractive::Fullscreen(); + + // -- 1. Render System selection + + int selectedRenders = 0; + auto renderSystemRadioBox = ftxui::Radiobox(&renderSystems, &selectedRenders); + // auto nextButton = ftxui::Button("Next", [&] { current = Pages::SelectRenderSystem; screen.PostEvent(ftxui::Event::Custom); }); + auto nextButton = ftxui::Button("Next", + [&] + { + current = Pages::SelectRenderSystem; + screen.Exit(); + }); + auto selectRenderSystemPage = ftxui::Container::Vertical({renderSystemRadioBox, nextButton}); + auto selectRSRenderer = Renderer(selectRenderSystemPage, + [&] + { + return vbox({ + text("Select Render System:") | bold, + renderSystemRadioBox->Render(), + separator(), + nextButton->Render(), + }) | + border | center; + }); + + // -- 2. Render System Settings selection + screen.Loop(selectRSRenderer); -//--------------------------------------------------------------------------------------------------------------------- + // auto testComponent = ftxui::Renderer(selectRSRenderer, + // [&]() + // { + // auto rendererWin = ftxui::window(ftxui::text("Render System"), render->Render() | ftxui::vscroll_indicator | ftxui::frame); + // return rendererWin; + // }); + // screen.Loop(testComponent); +} +// LCOV_EXCL_STOP } // namespace morpheus::vis diff --git a/libraries/visualisation/lib/morpheus/vis/render_system_factory.hpp b/libraries/visualisation/lib/morpheus/vis/render_system_factory.hpp index 8a83f49d..144c35ca 100644 --- a/libraries/visualisation/lib/morpheus/vis/render_system_factory.hpp +++ b/libraries/visualisation/lib/morpheus/vis/render_system_factory.hpp @@ -1,11 +1,10 @@ #pragma once -#include "morpheus/application/po/adapters/enum.hpp" #include #if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) #include -#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_WINDOWS) +#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_PC_WINDOWS) #include #endif // #if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) @@ -18,11 +17,14 @@ #include #include -#include - #include #include +namespace boost::program_options +{ +class options_description; +} + namespace morpheus::vis { @@ -54,17 +56,15 @@ class RenderSystemFactory /*! */ - RenderSystemFactory(); + RenderSystemFactory() = default; ///@} /// Register program options - void addOptions(boost::program_options::options_description& options) - { - namespace po = boost::program_options; - options.add_options()("render-system", po::value(&mActiveApi)->default_value(mActiveApi), "The rendering system to instantiate."); - } + void addOptions(boost::program_options::options_description& options); + + // boost::program_options::options_description& runTuiConfiguration(); + void runTuiConfiguration(); -private: /// Returns a map of the available rendering systems to their respective underlying graphics APIs. /// static constexpr auto availableAPIs() @@ -73,13 +73,14 @@ class RenderSystemFactory return hana::make_map( #if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) hana::make_pair(RenderSystemType, hana::type_c), -#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_WINDOWS) +#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_PC_WINDOWS) hana::make_pair(RenderSystemType, hana::type_c), #endif // #if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) hana::make_pair(RenderSystemType, hana::type_c>), hana::make_pair(RenderSystemType, hana::type_c)); } +private: // using APIMap = std::invoke_result_t; // constexpr static auto renderSystemAPIs = availableAPIs(); diff --git a/libraries/visualisation/tests/render_system_factory.tests.cpp b/libraries/visualisation/tests/render_system_factory.tests.cpp index 1246628c..8ab41111 100644 --- a/libraries/visualisation/tests/render_system_factory.tests.cpp +++ b/libraries/visualisation/tests/render_system_factory.tests.cpp @@ -1,8 +1,31 @@ #include -#include +#include namespace morpheus::vis { -TEST_CASE("A place holder test", "[vis.placeholder]") {} + +TEST_CASE("A place holder test", "[morpheus.vis.render_system_factory.available_apis]") +{ + namespace hana = boost::hana; + static constinit auto apis = RenderSystemFactory::availableAPIs(); + +#if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) + STATIC_REQUIRE(!hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); +#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_PC_WINDOWS) + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(!hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); +#elif (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_LINUX) + STATIC_REQUIRE(!hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(!hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); + STATIC_REQUIRE(hana::contains(apis, RenderSystemType)); +#endif // #if (MORPHEUS_BUILD_PLATFORM == MORPHEUS_TARGET_PLATFORM_APPLE) +} + } // namespace morpheus::vis