From bcbc581417843b543c3bcac99522d09a0ce23dc7 Mon Sep 17 00:00:00 2001 From: Matthew Nickerson <82531659+mwnickerson@users.noreply.github.com> Date: Sun, 17 May 2026 08:10:39 -0400 Subject: [PATCH] fix: harden CLI export without OpenGL thumbnails (#13532) ## Summary - skip CLI thumbnail generation when an OpenGL/GLFW context cannot be created, allowing export workflows to continue in headless/automation environments - guard filament variant remapping against missing config options and out-of-range variant indexes - log warnings instead of dereferencing invalid config state during CLI/profile-driven export ## Context These guards came out of automating OrcaSlicer CLI exports for printer workflows. In that flow, slicing/export can still be valid even when thumbnail rendering is unavailable, but the current path can proceed into OpenGL thumbnail setup after context creation fails. Separately, malformed or mismatched filament/profile state can index past option vectors during multi-filament value remapping. ## Test plan - `git diff --check` - `cmake --build build/arm64 --config RelWithDebInfo --target OrcaSlicer -- -j4` --- src/OrcaSlicer.cpp | 14 +++++++-- src/libslic3r/PrintConfig.cpp | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 08335b9196..880eaffa4b 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -6435,6 +6435,7 @@ int CLI::run(int argc, char **argv) glfwGetVersion(&gl_major, &gl_minor, &gl_verbos); BOOST_LOG_TRIVIAL(info) << boost::format("opengl version %1%.%2%.%3%")%gl_major %gl_minor %gl_verbos; + bool thumbnail_opengl_ready = false; glfwSetErrorCallback(glfw_callback); int ret = glfwInit(); if (ret == GLFW_FALSE) { @@ -6463,13 +6464,22 @@ int CLI::run(int argc, char **argv) GLFWwindow* window = glfwCreateWindow(640, 480, "base_window", NULL, NULL); if (window == NULL) { - BOOST_LOG_TRIVIAL(error) << "Failed to create GLFW window" << std::endl; + BOOST_LOG_TRIVIAL(error) << "Failed to create GLFW window; skipping thumbnail rendering for CLI export" << std::endl; } - else + else { glfwMakeContextCurrent(window); + thumbnail_opengl_ready = true; + } } //opengl manager related logic + if (!thumbnail_opengl_ready) { + BOOST_LOG_TRIVIAL(error) << "OpenGL context unavailable; skip thumbnail generating" << std::endl; + need_create_thumbnail_group = false; + need_create_no_light_group = false; + need_create_top_group = false; + } + else { GUI::OpenGLManager opengl_mgr; bool opengl_valid = opengl_mgr.init_gl(false); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 21f2a22c79..ac712818ca 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9589,11 +9589,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coStrings: { ConfigOptionStrings * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9602,11 +9610,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coInts: { ConfigOptionInts * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9615,11 +9631,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coFloats: { ConfigOptionFloats * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9628,11 +9652,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coPercents: { ConfigOptionPercents * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9641,11 +9673,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coFloatsOrPercents: { ConfigOptionFloatsOrPercents * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9654,11 +9694,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coBools: { ConfigOptionBools * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values; @@ -9667,11 +9715,19 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen case coEnums: { ConfigOptionEnumsGeneric * opt = this->option(key); + if (!opt) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found, skipping")%__LINE__%key; + break; + } std::vector new_values; new_values.resize(filament_count); for (int f_index = 0; f_index < filament_count; f_index++) { + if (variant_index[f_index] < 0 || static_cast(variant_index[f_index]) >= opt->size()) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% variant index %3% out of range, skipping")%__LINE__%key%variant_index[f_index]; + continue; + } new_values[f_index] = opt->get_at(variant_index[f_index]); } opt->values = new_values;