From c6d1c11ebb0fe42fe95249e839c21e9464b478c6 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sun, 22 Feb 2026 10:04:58 +0800 Subject: [PATCH] Fix time estimation using wrong machine limits due to broken extruder_id indexing The extruder_id*2 offset in get_axis_max_feedrate/get_axis_max_acceleration was cherry-picked from BambuStudio's per-nozzle limit system, which OrcaSlicer never ported. Without that system the limit arrays only have 2 values ([0]=Normal, [1]=Stealth), so any extruder_id > 0 or the uninitialized value (255) would overshoot the array and fall back to values.back(), always returning stealth-mode limits and producing incorrect time estimates. Revert to indexing by time mode only (matching v2.3.1 behavior) and simplify the M201/M203 handlers to write only the two mode slots they actually use. --- src/libslic3r/GCode/GCodeProcessor.cpp | 77 +++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 7 ++- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 8bf39eb715..4ca14db743 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -4167,7 +4167,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); if (curr.abs_axis_feedrate[a] != 0.0f) { - float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a), m_extruder_id); + float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); } } @@ -4191,7 +4191,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes //BBS for (unsigned char a = X; a <= E; ++a) { - float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a), m_extruder_id); + float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance); } @@ -4525,7 +4525,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line) curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); if (curr.abs_axis_feedrate[a] != 0.0f) { - float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a), m_extruder_id); + float axis_max_feedrate = get_axis_max_feedrate(static_cast(i), static_cast(a)); if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); } } @@ -4549,7 +4549,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line) //BBS for (unsigned char a = X; a <= E; ++a) { - float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a), m_extruder_id); + float axis_max_acceleration = get_axis_max_acceleration(static_cast(i), static_cast(a)); if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance); } @@ -5252,18 +5252,17 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) { // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; - int indx_limit = m_time_processor.machine_limits.machine_max_acceleration_x.size() / 2; - for (size_t index = 0; index < indx_limit; index += 2) { - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { - if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, index + i, line.x() * factor); - if (line.has_y()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, index + i, line.y() * factor); + // Write to index i (0=Normal, 1=Stealth) — matches get_axis_max_acceleration's read pattern. + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { + if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); - if (line.has_z()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, index + i, line.z() * factor); + if (line.has_y()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor); - if (line.has_e()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, index + i, line.e() * factor); - } + if (line.has_z()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor); + + if (line.has_e()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor); } } } @@ -5278,23 +5277,20 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line) // http://smoothieware.org/supported-g-codes float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie || m_flavor == gcfKlipper) ? 1.0f : MMMIN_TO_MMSEC; - //BBS: - int indx_limit = m_time_processor.machine_limits.machine_max_speed_x.size() / 2; - for (size_t index = 0; index < indx_limit; index += 2) { - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { - if (line.has_x()) - set_option_value(m_time_processor.machine_limits.machine_max_speed_x, index + i, line.x() * factor); + // Write to index i (0=Normal, 1=Stealth) — matches get_axis_max_feedrate's read pattern. + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + if (static_cast(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) { + if (line.has_x()) + set_option_value(m_time_processor.machine_limits.machine_max_speed_x, i, line.x() * factor); - if (line.has_y()) - set_option_value(m_time_processor.machine_limits.machine_max_speed_y, index + i, line.y() * factor); + if (line.has_y()) + set_option_value(m_time_processor.machine_limits.machine_max_speed_y, i, line.y() * factor); - if (line.has_z()) - set_option_value(m_time_processor.machine_limits.machine_max_speed_z, index + i, line.z() * factor); + if (line.has_z()) + set_option_value(m_time_processor.machine_limits.machine_max_speed_z, i, line.z() * factor); - if (line.has_e()) - set_option_value(m_time_processor.machine_limits.machine_max_speed_e, index + i, line.e() * factor); - } + if (line.has_e()) + set_option_value(m_time_processor.machine_limits.machine_max_speed_e, i, line.e() * factor); } } } @@ -5735,28 +5731,31 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMod return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast(mode))); } -float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const +// Machine limit arrays hold 2 values: [0]=Normal, [1]=Stealth. Index by mode only. +// BambuStudio used extruder_id*2+mode to support per-nozzle limits, but OrcaSlicer +// never ported that system (filament_map_2 / get_config_idx_for_filament), so the +// extruder_id offset was always wrong: uninitialized extruder (255) or extruder > 0 +// would overshoot the array and fall back to values.back() (stealth limits). +float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { - int matched_pos = extruder_id * 2; switch (axis) { - case X: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_x, matched_pos + static_cast(mode)); } - case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_y, matched_pos + static_cast(mode)); } - case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_z, matched_pos + static_cast(mode)); } - case E: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_e, matched_pos + static_cast(mode)); } + case X: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_x, static_cast(mode)); } + case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_y, static_cast(mode)); } + case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_z, static_cast(mode)); } + case E: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_e, static_cast(mode)); } default: { return 0.0f; } } } -float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const +float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { - int matched_pos = extruder_id * 2; switch (axis) { - case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, matched_pos + static_cast(mode)); } - case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, matched_pos + static_cast(mode)); } - case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, matched_pos + static_cast(mode)); } - case E: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, matched_pos + static_cast(mode)); } + case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, static_cast(mode)); } + case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, static_cast(mode)); } + case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, static_cast(mode)); } + case E: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, static_cast(mode)); } default: { return 0.0f; } } } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 06a448eac2..1e17086c60 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -1059,8 +1059,11 @@ class Print; float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; - float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const; - float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const; + // Machine limit arrays are indexed by time mode only: [0]=Normal, [1]=Stealth. + // Do NOT add an extruder_id parameter — OrcaSlicer does not use BambuStudio's + // per-nozzle machine limits (filament_map_2 / get_config_idx_for_filament). + float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; + float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; Vec3f get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const; float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;