diff --git a/application/F3DOptionsTools.cxx b/application/F3DOptionsTools.cxx index 3ab9732af8..24f0f7ef08 100644 --- a/application/F3DOptionsTools.cxx +++ b/application/F3DOptionsTools.cxx @@ -64,12 +64,13 @@ static inline const std::array CLIOptions = {{ { { "output", "", "Render to file", "", "" }, { "no-background", "", "No background when render to file", "", "1" }, { "help", "h", "Print help", "", "" }, { "version", "", "Print version details", "", "" }, - { "readers-list", "", "Print the list of readers", "", "" }, - { "bindings-list", "", "Print the list of interaction bindings and exits, ignored with `--no-render`, only considers the first file group.", "", "" }, + { "list-readers", "", "Print the list of readers", "", "" }, + { "list-bindings", "", "Print the list of interaction bindings and exits, ignored with `--no-render`, only considers the first file group.", "", "" }, { "config", "", "Specify the configuration file to use. absolute/relative path or filename/filestem to search in configuration file locations", "", "" }, { "dry-run", "", "Do not read the configuration file", "", "1" }, { "no-render", "", "Do not read the configuration file", "", "1" }, { "rendering-backend", "", "Backend to use when rendering (auto|glx|wgl|egl|osmesa)", "", "" }, + { "list-rendering-backends", "", "Print the list of rendering backends available on this system", "", "" }, { "max-size", "", "Maximum size in Mib of a file to load, leave empty for unlimited", "", "" }, #if F3D_MODULE_DMON { "watch", "", "Watch current file and automatically reload it whenever it is modified on disk", "", "1" }, @@ -175,7 +176,7 @@ static inline const std::array CLIOptions = {{ * True boolean options need to be filtered out in ParseCLIOptions * This is the easiest, compile time way to do it */ -constexpr std::array CLIBooleans = {"version", "help", "readers-list", "scan-plugins"}; +constexpr std::array CLIBooleans = {"version", "help", "list-readers", "scan-plugins", "list-rendering-backends"}; //---------------------------------------------------------------------------- /** @@ -269,6 +270,19 @@ void PrintVersion() f3d::log::setUseColoring(true); } +//---------------------------------------------------------------------------- +void PrintRenderingBackendList() +{ + auto backends = f3d::engine::getRenderingBackendList(); + + f3d::log::setUseColoring(false); + f3d::log::info("Rendering backends:"); + for (const auto& [name, available] : backends) + { + f3d::log::info(name + ": " + (available ? "available" : "unavailable")); + } +} + //---------------------------------------------------------------------------- void PrintReadersList() { @@ -484,14 +498,19 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions( ::PrintVersion(); throw F3DExNoProcess("version requested"); } + if (result.count("list-rendering-backends") > 0) + { + ::PrintRenderingBackendList(); + throw F3DExNoProcess("rendering backend list requested"); + } if (result.count("scan-plugins") > 0) { ::PrintPluginsScan(); throw F3DExNoProcess("scan plugins requested"); } - if (result.count("readers-list") > 0) + if (result.count("list-readers") > 0) { - // `--readers-list` needs plugin to be loaded to be useful + // `--list-readers` needs plugin to be loaded to be useful // Load them manually std::vector plugins; if (result.count("load-plugins") > 0) diff --git a/application/F3DOptionsTools.h b/application/F3DOptionsTools.h index 4eb5e55733..3f40b032f8 100644 --- a/application/F3DOptionsTools.h +++ b/application/F3DOptionsTools.h @@ -27,7 +27,7 @@ using OptionsEntries = std::vector; static inline const OptionsDict DefaultAppOptions = { { "input", "" }, { "output", "" }, - { "bindings-list", "false" }, + { "list-bindings", "false" }, { "no-background", "false" }, { "config", "" }, { "dry-run", "false" }, diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index fc09605731..a3ffc1ec6a 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -543,7 +543,7 @@ class F3DStarter::F3DInternals { // Update typed app options from app options this->AppOptions.Output = f3d::options::parse(appOptions.at("output")); - this->AppOptions.BindingsList = f3d::options::parse(appOptions.at("bindings-list")); + this->AppOptions.BindingsList = f3d::options::parse(appOptions.at("list-bindings")); this->AppOptions.NoBackground = f3d::options::parse(appOptions.at("no-background")); this->AppOptions.NoRender = f3d::options::parse(appOptions.at("no-render")); this->AppOptions.RenderingBackend = diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index ac5fe9d61d..9a5d3028ff 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -1030,9 +1030,9 @@ f3d_test(NAME TestConfigFileNoOptions DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/test # Test update interaction verbose f3d_test(NAME TestConfigFileBindingsVerbose DATA dragon.vtu ARGS --verbose CONFIG ${F3D_SOURCE_DIR}/testing/configs/bindings.json REGEXP "'Shift.O' : '`toggle model.point_sprites.enable` '" NO_BASELINE) -# Test bindings-list display with config file -f3d_test(NAME TestConfigFileBindingsList ARGS --bindings-list CONFIG ${F3D_SOURCE_DIR}/testing/configs/bindings.json REGEXP "Ctrl.Shift.O `toggle ui.filename`" NO_BASELINE) -f3d_test(NAME TestConfigFileBindingsListData DATA dragon.vtu ARGS --bindings-list CONFIG ${F3D_SOURCE_DIR}/testing/configs/bindings.json REGEXP "Any.3 `roll_camera 90`" NO_BASELINE) +# Test list-bindings display with config file +f3d_test(NAME TestConfigFileBindingsList ARGS --list-bindings CONFIG ${F3D_SOURCE_DIR}/testing/configs/bindings.json REGEXP "Ctrl.Shift.O `toggle ui.filename`" NO_BASELINE) +f3d_test(NAME TestConfigFileBindingsListData DATA dragon.vtu ARGS --list-bindings CONFIG ${F3D_SOURCE_DIR}/testing/configs/bindings.json REGEXP "Any.3 `roll_camera 90`" NO_BASELINE) # Test invalid value in config file f3d_test(NAME TestConfigFileInvalidValue DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/invalid_value.json REGEXP "must be a string, a boolean or a number" NO_BASELINE) @@ -1056,19 +1056,19 @@ f3d_test(NAME TestHelpPositional ARGS --help REGEXP "file1 file2 \.\.\.") # Test version display f3d_test(NAME TestVersion ARGS --version REGEXP "Version:") -# Test readers-list display -f3d_test(NAME TestReadersList ARGS --readers-list REGEXP_FAIL "No registered reader found") +# Test list-readers display +f3d_test(NAME TestReadersList ARGS --list-readers REGEXP_FAIL "No registered reader found") # Test invalid component string coverage f3d_test(NAME TestInteractionInvalidComponent INTERACTION UI DATA cow.vtp ARGS --coloring-component=1 NO_BASELINE) #H -# Test multi plugin readers-lists +# Test multi plugin list-readers if(F3D_PLUGIN_BUILD_ALEMBIC AND F3D_PLUGIN_BUILD_ASSIMP) - f3d_test(NAME TestReadersListMultiplePlugins ARGS --readers-list --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load") + f3d_test(NAME TestReadersListMultiplePlugins ARGS --list-readers --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load") endif() -# Test bindings-list display -f3d_test(NAME TestBindingsList ARGS --bindings-list REGEXP "Any.Question Print scene descr to terminal") +# Test list-bindings display +f3d_test(NAME TestBindingsList ARGS --list-bindings REGEXP "Any.Question Print scene descr to terminal") # Test rendering backends # For some reason the sanitizer detects leaks because of EGL and OSMesa @@ -1144,6 +1144,25 @@ f3d_test(NAME TestHelpPrecedenceWithUnknownOption ARGS --help --unknown REGEXP " # Test that --version is displayed even when there is an unknown option f3d_test(NAME TestVersionPrecedenceWithUnknownOption ARGS --version --unknown REGEXP "Version:" NO_BASELINE) +# Test rendering backend list +if(WIN32) + f3d_test(NAME TestRenderingBackenListWGL ARGS --list-rendering-backends NO_RENDER NO_BASELINE REGEXP "wgl: available") +elseif(APPLE) + f3d_test(NAME TestRenderingBackenListCOCOA ARGS --list-rendering-backends NO_RENDER NO_BASELINE REGEXP "cocoa: available") +endif() + +if(F3D_TESTING_ENABLE_GLX_TESTS AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914) + f3d_test(NAME TestRenderingBackenListGLX ARGS --list-rendering-backends NO_RENDER NO_BASELINE REGEXP "glx: available") +endif() + +if(F3D_TESTING_ENABLE_EGL_TESTS AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914) + f3d_test(NAME TestRenderingBackenListEGL ARGS --list-rendering-backends NO_RENDER NO_BASELINE REGEXP "egl: available") +endif() + +if(F3D_TESTING_ENABLE_OSMESA_TESTS) + f3d_test(NAME TestRenderingBackenListOSMesa ARGS --list-rendering-backends NO_RENDER NO_BASELINE REGEXP "osmesa: available") +endif() + # Test scan plugins if(NOT F3D_MACOS_BUNDLE) f3d_test(NAME TestScanPluginsCheckNative ARGS --scan-plugins NO_RENDER NO_BASELINE REGEXP " - native") diff --git a/doc/user/CONFIGURATION_FILE.md b/doc/user/CONFIGURATION_FILE.md index 997f2a465d..768ff81fa7 100644 --- a/doc/user/CONFIGURATION_FILE.md +++ b/doc/user/CONFIGURATION_FILE.md @@ -65,7 +65,7 @@ The third block specifies raytracing usage for .gltf and .glb files. The last block specifies that volume rendering should be used with .mhd files. The following options cannot be set via config file: -`help`, `version`, `readers-list`, `config`, `dry-run` and `input`. +`help`, `version`, `list-readers`, `list-rendering-backends`, `scan-plugins`, `config`, `dry-run` and `input`. The following options are only taken on the first load: `no-render`, `output`, `position`, `resolution`, `frame-rate` and all testing options. @@ -115,7 +115,7 @@ interaction on the `Any+3` bind and even define a bindings that have multiple co on the `Ctrl+O` bind. Please note this configuration feature is only available through config file and not through the command line. -However, it is possible to check your current binding configuration by using the `--bindings-list` CLI options. +However, it is possible to check your current binding configuration by using the `--list-bindings` CLI options. ### Bind diff --git a/doc/user/OPTIONS.md b/doc/user/OPTIONS.md index 15cc4be8ee..aa17cd7a76 100644 --- a/doc/user/OPTIONS.md +++ b/doc/user/OPTIONS.md @@ -13,7 +13,9 @@ Options|Default|Description \-\-no-background||Use with \-\-output to output a png file with a transparent background. -h, \-\-help||Print *help* and exit. Ignore `--verbose`. \-\-version||Show *version* information and exit. Ignore `--verbose`. -\-\-readers-list||List available *readers* and exit. Ignore `--verbose`. +\-\-list-readers||List available *readers* and exit. Ignore `--verbose`. +\-\-list-bindings||List available *bindings* and exit. Ignore `--verbose`. +\-\-list-rendering-backends||List available *rendering backends* and exit. Ignore `--verbose`. \-\-config=\|config|Specify the [configuration file](CONFIGURATION_FILE.md) to use. Supports absolute/relative path but also filename/filestem to search for in standard configuration file locations. \-\-dry-run||Do not read any configuration file and consider only the command line options. \-\-no-render||Do not render anything and quit just after loading the first file, use with \-\-verbose to recover information about a file. diff --git a/library/public/engine.h b/library/public/engine.h index 230b3f18a3..723e619489 100644 --- a/library/public/engine.h +++ b/library/public/engine.h @@ -198,6 +198,12 @@ class F3D_EXPORT engine */ interactor& getInteractor(); + /** + * List rendering backends supported by libf3d. + * All backends have an associated boolean flag indicating if it can be used. + */ + static std::map getRenderingBackendList(); + /** * Load a plugin. * Supports full path, relative path, and plugin name. diff --git a/library/src/engine.cxx b/library/src/engine.cxx index 1a7a95b17e..f567b6a098 100644 --- a/library/src/engine.cxx +++ b/library/src/engine.cxx @@ -25,6 +25,19 @@ namespace f3d class engine::internals { public: + template + static bool BackendAvailable(F&& func) + { + try + { + return func() != nullptr; + } + catch (const context::loading_exception&) + { + return false; + } + } + std::unique_ptr Options; std::unique_ptr Window; std::unique_ptr Scene; @@ -212,6 +225,20 @@ interactor& engine::getInteractor() return *this->Internals->Interactor; } +//---------------------------------------------------------------------------- +std::map engine::getRenderingBackendList() +{ + std::map backends; + + backends["glx"] = engine::internals::BackendAvailable(context::glx); + backends["wgl"] = engine::internals::BackendAvailable(context::wgl); + backends["cocoa"] = engine::internals::BackendAvailable(context::cocoa); + backends["egl"] = engine::internals::BackendAvailable(context::egl); + backends["osmesa"] = engine::internals::BackendAvailable(context::osmesa); + + return backends; +} + //---------------------------------------------------------------------------- void engine::loadPlugin(const std::string& pathOrName, const std::vector& searchPaths) { diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 480335315d..96c54a7f98 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -404,7 +404,8 @@ PYBIND11_MODULE(pyf3d, module) "autoload_plugins", &f3d::engine::autoloadPlugins, "Automatically load internal plugins") .def_static("get_plugins_list", &f3d::engine::getPluginsList) .def_static("get_lib_info", &f3d::engine::getLibInfo, py::return_value_policy::reference) - .def_static("get_readers_info", &f3d::engine::getReadersInfo); + .def_static("get_readers_info", &f3d::engine::getReadersInfo) + .def_static("get_rendering_backend_list", &f3d::engine::getRenderingBackendList); // libInformation py::class_(module, "LibInformation") diff --git a/python/testing/test_engine.py b/python/testing/test_engine.py index a8f091a782..95009f464c 100644 --- a/python/testing/test_engine.py +++ b/python/testing/test_engine.py @@ -62,3 +62,9 @@ def test_get_readers_info(): assert isinstance(reader.plugin_name, str) and reader.plugin_name assert isinstance(reader.has_scene_reader, bool) assert isinstance(reader.has_geometry_reader, bool) + + +def test_get_rendering_backend_list(): + backends = f3d.Engine.get_rendering_backend_list() + + assert isinstance(backends, dict) and len(backends) == 5