Files
OrcaSlicer/FILAMENT_EXTRUDER_ANALYSIS_FINAL.md
xiaoyeliu d15a146329 Add Filament_Extruder_map (#160)
* Optimize: extruder_filament_map

* Fix: {print_time}
2026-02-09 20:29:53 +08:00

153 lines
6.2 KiB
Markdown

# Filament-Extruder Mapping Analysis - Final Findings
## The Key Discovery
After investigating the original implementation vs the optimized version, I found the **critical missing piece**: `initialize_filament_extruder_map()`.
## How It Actually Works
### The Initialization Process
**Called in PrintApply.cpp during apply():**
```cpp
// PrintApply.cpp line 1276
this->initialize_filament_extruder_map();
```
**Implementation in Print.cpp (lines 497-544):**
```cpp
void Print::initialize_filament_extruder_map()
{
m_filament_extruder_map.clear();
// Get the number of physical extruders
size_t physical_extruder_count = m_config.nozzle_diameter.values.size();
// Get ALL configured filaments (not just used ones)
std::vector<unsigned int> filament_extruders = this->extruders();
if (filament_extruders.empty()) {
size_t filament_count = m_config.filament_diameter.size();
for (size_t i = 0; i < filament_count; ++i) {
filament_extruders.push_back((unsigned int)i);
}
}
// Create mapping: filament_id -> physical_extruder_id
// Mapping formula: physical_extruder = filament_id % physical_extruder_count
for (unsigned int filament_idx : filament_extruders) {
int physical_extruder = filament_idx % physical_extruder_count;
m_filament_extruder_map[filament_idx] = physical_extruder;
}
}
```
### The Result
After initialization:
- Filament 0 → Extruder 0
- Filament 1 → Extruder 1
- Filament 2 → Extruder 2
- Filament 3 → Extruder 3
- Filament 4 → Extruder 0 (modulo)
- Filament 5 → Extruder 1 (modulo)
- Filament 6 → Extruder 2 (modulo)
- Filament 7 → Extruder 3 (modulo)
**The map is ALWAYS populated before slicing!**
## Why Both Versions Work
### Original Implementation (9d423d0714)
**get_physical_extruder():**
```cpp
int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx);
// Fallback: identity mapping (filament 5 → extruder 5)
int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx;
return physical_extruder_id;
}
```
**Why it worked:** The fallback logic was **never reached** because `initialize_filament_extruder_map()` always populates the map with modulo mapping.
### Optimized Implementation (989a53e124)
**get_physical_extruder():**
```cpp
int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx);
if (it != m_filament_extruder_map.end()) {
return it->second;
} else {
// Fallback: modulo mapping (filament 5 → extruder 1)
size_t physical_count = m_config.nozzle_diameter.values.size();
return filament_idx % physical_count;
}
}
```
**Why it also works:** Same reason - the map is always populated. The fallback is just defensive programming.
## What Actually Changed
The key difference between the two versions is **NOT** the behavior during normal operation (both work the same), but:
1. **Defensive programming:** The optimized version has a safer fallback (modulo instead of identity)
2. **Code simplification:** The optimized version removed some redundant physical_extruder calculations in GCode.cpp
3. **Better logging:** The optimized version has more detailed logging
4. **Validation:** The optimized version added bounds checking in PrintApply.cpp
## The Real Issue: Why 3MF Slicing Might Fail
If the user is experiencing issues with >4 filament 3MF slicing, it's **NOT** because of the identity vs modulo fallback difference (since that code path is never reached).
Possible causes:
1. **initialize_filament_extruder_map() not being called:** Check if there's a code path that bypasses Print::apply()
2. **Config array access using filament_id instead of physical_extruder_id:** The optimized version removed some explicit physical_extruder calculations in GCode.cpp. If those changes introduced direct filament_id access to config arrays, that would cause crashes.
3. **3MF file format issues:** The 3MF file might have inconsistent filament/extruder configurations
4. **Placeholder replacement:** GCode placeholder macros might still be using filament_id instead of physical_extruder_id
## Code Changes That Matter
### Potentially Problematic Change in GCode.cpp
**Optimized version removed explicit physical_extruder calculation:**
```cpp
// BEFORE (9d423d0714):
int previous_physical_extruder = (previous_extruder_id >= 0) ?
gcode_writer.get_physical_extruder(previous_extruder_id) : -1;
int new_physical_extruder = gcode_writer.get_physical_extruder(new_extruder_id);
float old_retract_length = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ?
full_config.retraction_length.get_at(previous_physical_extruder) : 0;
// AFTER (989a53e124):
float old_retract_length = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ?
full_config.retraction_length.get_at(previous_extruder_id) : 0; // Uses filament_id directly!
```
**This is a BUG!** The optimized version uses `previous_extruder_id` (filament_id) directly to access `retraction_length` array. This will cause array out-of-bounds access when filament_id >= physical_extruder_count.
## Recommendation
The optimized version (989a53e124) may have introduced regressions by removing explicit physical_extruder calculations. Need to:
1. **Audit all config array access** in GCode.cpp to ensure physical_extruder_id is used
2. **Add bounds checking** or use the PHYSICAL_EXTRUDER_CONFIG macro consistently
3. **Test with 8-filament 3MF** on 4-extruder configuration
## Summary
| Aspect | Original (9d423d0714) | Optimized (989a53e124) |
|--------|----------------------|------------------------|
| **Modulo mapping** | ✓ Via initialize_filament_extruder_map() | ✓ Via initialize_filament_extruder_map() |
| **Fallback logic** | Identity (never used) | Modulo (never used) |
| **Config array access** | Explicit physical_extruder calculation | Some direct filament_id access (BUG?) |
| **Safety** | Good | Potentially introduced bugs |
**Bottom line:** Both versions use the same modulo mapping via `initialize_filament_extruder_map()`, but the optimized version may have introduced bugs by simplifying config array access.