diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp
index 5ab4c76ef..e54599ca6 100644
--- a/src/libslic3r/CustomGCode.hpp
+++ b/src/libslic3r/CustomGCode.hpp
@@ -42,7 +42,7 @@ enum Mode
     SingleExtruder,   // Single extruder printer preset is selected
     MultiAsSingle,    // Multiple extruder printer preset is selected, but 
                       // this mode works just for Single extruder print 
-                      // (For all print from objects settings is used just one extruder) 
+                      // (The same extruder is assigned to all ModelObjects and ModelVolumes).
     MultiExtruder     // Multiple extruder printer preset is selected
 };
 
diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index b7725d11d..d66aa8f01 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -105,6 +105,7 @@ public:
     coordf_t            slice_z;       // Z used for slicing in unscaled coordinates
     coordf_t            print_z;       // Z used for printing in unscaled coordinates
     coordf_t            height;        // layer height in unscaled coordinates
+    coordf_t            bottom_z() const { return this->print_z - this->height; }
 
     // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry 
     // (with possibly differing extruder ID and slicing parameters) and merged.
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index ebb05772f..b3b686f03 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -168,6 +168,17 @@ void PrintConfigDef::init_fff_params()
     def->min = 0;
     def->set_default_value(new ConfigOptionInt(3));
 
+    def = this->add("bottom_solid_min_thickness", coFloat);
+    //TRN To be shown in Print Settings "Top solid layers"
+    def->label = L("Bottom");
+    def->category = L("Layers and Perimeters");
+    def->tooltip = L("The number of bottom solid layers is increased above bottom_solid_layers if necessary to satisfy "
+    				 "minimum thickness of bottom shell.");
+    def->full_label = L("Minimum bottom shell thickness");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->set_default_value(new ConfigOptionFloat(0.));
+
     def = this->add("bridge_acceleration", coFloat);
     def->label = L("Bridge");
     def->tooltip = L("This is the acceleration your printer will use for bridges. "
@@ -1782,6 +1793,13 @@ void PrintConfigDef::init_fff_params()
     def->shortcut.push_back("bottom_solid_layers");
     def->min = 0;
 
+    def = this->add("solid_min_thickness", coFloat);
+    def->label = L("Minimum thickness of a top / bottom shell");
+    def->tooltip = L("Minimum thickness of a top / bottom shell");
+    def->shortcut.push_back("top_solid_min_thickness");
+    def->shortcut.push_back("bottom_solid_min_thickness");
+    def->min = 0;
+
     def = this->add("spiral_vase", coBool);
     def->label = L("Spiral vase");
     def->tooltip = L("This feature will raise Z gradually while printing a single-walled object "
@@ -2128,6 +2146,18 @@ void PrintConfigDef::init_fff_params()
     def->min = 0;
     def->set_default_value(new ConfigOptionInt(3));
 
+    def = this->add("top_solid_min_thickness", coFloat);
+    //TRN To be shown in Print Settings "Top solid layers"
+    def->label = L("Top");
+    def->category = L("Layers and Perimeters");
+    def->tooltip = L("The number of top solid layers is increased above top_solid_layers if necessary to satisfy "
+    				 "minimum thickness of top shell."
+    				 " This is useful to prevent pillowing effect when printing with variable layer height.");
+    def->full_label = L("Minimum top shell thickness");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->set_default_value(new ConfigOptionFloat(0.));
+
     def = this->add("travel_speed", coFloat);
     def->label = L("Travel");
     def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 5130d3b05..c854feafc 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -466,6 +466,7 @@ class PrintRegionConfig : public StaticPrintConfig
 public:
     ConfigOptionFloat               bridge_angle;
     ConfigOptionInt                 bottom_solid_layers;
+    ConfigOptionFloat               bottom_solid_min_thickness;
     ConfigOptionFloat               bridge_flow_ratio;
     ConfigOptionFloat               bridge_speed;
     ConfigOptionBool                ensure_vertical_shell_thickness;
@@ -501,6 +502,7 @@ public:
     ConfigOptionBool                thin_walls;
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
+    ConfigOptionFloat 				top_solid_min_thickness;
     ConfigOptionFloatOrPercent      top_solid_infill_speed;
     ConfigOptionBool                wipe_into_infill;
 
@@ -509,6 +511,7 @@ protected:
     {
         OPT_PTR(bridge_angle);
         OPT_PTR(bottom_solid_layers);
+        OPT_PTR(bottom_solid_min_thickness);
         OPT_PTR(bridge_flow_ratio);
         OPT_PTR(bridge_speed);
         OPT_PTR(ensure_vertical_shell_thickness);
@@ -542,6 +545,7 @@ protected:
         OPT_PTR(top_infill_extrusion_width);
         OPT_PTR(top_solid_infill_speed);
         OPT_PTR(top_solid_layers);
+        OPT_PTR(top_solid_min_thickness);
         OPT_PTR(wipe_into_infill);
     }
 };
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 5dcaf8dfb..2ff361309 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -507,7 +507,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
             || opt_key == "infill_every_layers"
             || opt_key == "solid_infill_every_layers"
             || opt_key == "bottom_solid_layers"
+            || opt_key == "bottom_solid_min_thickness"
             || opt_key == "top_solid_layers"
+            || opt_key == "top_solid_min_thickness"
             || opt_key == "solid_infill_below_area"
             || opt_key == "infill_extruder"
             || opt_key == "solid_infill_extruder"
@@ -914,6 +916,19 @@ void PrintObject::discover_vertical_shells()
         Polygons    bottom_surfaces;
         Polygons    holes;
     };
+    coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
+    // Does this region possibly produce more than 1 top or bottom layer?
+    auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
+	    auto num_extra_layers = [min_layer_height](int num_solid_layers, coordf_t min_shell_thickness) {
+	    	if (num_solid_layers == 0)
+	    		return 0;
+	    	int n = num_solid_layers - 1;
+	    	int n2 = int(ceil(min_shell_thickness / min_layer_height));
+	    	return std::max(n, n2 - 1);
+	    };
+    	return num_extra_layers(config.top_solid_layers, config.top_solid_min_thickness) +
+	    	   num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
+    };
     std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry());
     bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
     if (top_bottom_surfaces_all_regions) {
@@ -921,11 +936,11 @@ void PrintObject::discover_vertical_shells()
         // is calculated over all materials.
         // Is the "ensure vertical wall thickness" applicable to any region?
         bool has_extra_layers = false;
-        for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
-            const PrintRegion &region = *m_print->get_region(idx_region);
-            if (region.config().ensure_vertical_shell_thickness.value && 
-                (region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
+        for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
+            const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
+            if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
                 has_extra_layers = true;
+                break;
             }
         }
         if (! has_extra_layers)
@@ -1006,9 +1021,7 @@ void PrintObject::discover_vertical_shells()
         if (! region.config().ensure_vertical_shell_thickness.value)
             // This region will be handled by discover_horizontal_shells().
             continue;
-        int n_extra_top_layers    = std::max(0, region.config().top_solid_layers.value - 1);
-        int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1);
-        if (n_extra_top_layers + n_extra_bottom_layers == 0)
+        if (! has_extra_layers_fn(region.config()))
             // Zero or 1 layer, there is no additional vertical wall thickness enforced.
             continue;
 
@@ -1049,7 +1062,7 @@ void PrintObject::discover_vertical_shells()
         BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness";
         tbb::parallel_for(
             tbb::blocked_range<size_t>(0, m_layers.size(), grain_size),
-            [this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions]
+            [this, idx_region, &cache_top_botom_regions]
             (const tbb::blocked_range<size_t>& range) {
                 // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
                 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@@ -1060,8 +1073,9 @@ void PrintObject::discover_vertical_shells()
         			++ debug_idx;
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 
-                    Layer       *layer               = m_layers[idx_layer];
-                    LayerRegion *layerm              = layer->m_regions[idx_region];
+                    Layer       	        *layer          = m_layers[idx_layer];
+                    LayerRegion 	        *layerm         = layer->m_regions[idx_region];
+                    const PrintRegionConfig &region_config  = layerm->region()->config();
 
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
                     layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
@@ -1101,30 +1115,47 @@ void PrintObject::discover_vertical_shells()
                             }
                         }
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-                        // Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
-                        bool hole_first = true;
-                        for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n)
-                            if (n >= 0 && n < (int)m_layers.size()) {
-                                const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n];
-                                if (hole_first) {
-                                    hole_first = false;
-                                    polygons_append(holes, cache.holes);
-                                }
-                                else if (! holes.empty()) {
-                                    holes = intersection(holes, cache.holes);
-                                }
-                                size_t n_shell_old = shell.size();
-                                if (n > int(idx_layer))
-                                    // Collect top surfaces.
-                                    polygons_append(shell, cache.top_surfaces);
-                                else if (n < int(idx_layer))
-                                    // Collect bottom and bottom bridge surfaces.
-                                    polygons_append(shell, cache.bottom_surfaces);
-                                // Running the union_ using the Clipper library piece by piece is cheaper 
-                                // than running the union_ all at once.
-                                if (n_shell_old < shell.size())
-                                   shell = union_(shell, false);
-                            }
+			        	polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
+                        {
+                            // Gather top regions projected to this layer.
+                            coordf_t print_z      = layer->print_z;
+                            int      n_top_layers = region_config.top_solid_layers.value;
+	                        for (int i = int(idx_layer) + 1; 
+	                        	i < int(m_layers.size()) && 
+	                        		(i < int(idx_layer) + n_top_layers ||
+	                        		 m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
+	                        	++ i) {
+	                            const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
+								if (! holes.empty())
+									holes = intersection(holes, cache.holes);
+								if (! cache.top_surfaces.empty()) {
+		                            polygons_append(shell, cache.top_surfaces);
+		                            // Running the union_ using the Clipper library piece by piece is cheaper 
+		                            // than running the union_ all at once.
+	                               shell = union_(shell, false);
+	                           }
+	                        }
+	                    }
+	                    {
+                            // Gather bottom regions projected to this layer.
+                            coordf_t bottom_z        = layer->bottom_z();
+				        	int 	 n_bottom_layers = region_config.bottom_solid_layers.value;
+	                        for (int i = int(idx_layer) - 1;
+	                        	i >= 0 &&
+	                        		(i > int(idx_layer) - n_bottom_layers ||
+	                        		 bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
+	                        	-- i) {
+	                            const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
+								if (! holes.empty())
+									holes = intersection(holes, cache.holes);
+								if (! cache.bottom_surfaces.empty()) {
+		                            polygons_append(shell, cache.bottom_surfaces);
+		                            // Running the union_ using the Clipper library piece by piece is cheaper 
+		                            // than running the union_ all at once.
+		                            shell = union_(shell, false);
+		                        }
+	                        }
+	                    }
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
                         {
         					Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
@@ -2280,7 +2311,8 @@ void PrintObject::discover_horizontal_shells()
     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
         for (size_t i = 0; i < m_layers.size(); ++ i) {
             m_print->throw_if_canceled();
-            LayerRegion             *layerm = m_layers[i]->regions()[region_id];
+            Layer 					*layer  = m_layers[i];
+            LayerRegion             *layerm = layer->regions()[region_id];
             const PrintRegionConfig &region_config = layerm->region()->config();
             if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
                 (i % region_config.solid_infill_every_layers) == 0) {
@@ -2295,6 +2327,8 @@ void PrintObject::discover_horizontal_shells()
             if (region_config.ensure_vertical_shell_thickness.value)
                 continue;
             
+            coordf_t print_z  = layer->print_z;
+            coordf_t bottom_z = layer->bottom_z();
             for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
                 m_print->throw_if_canceled();
                 SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
@@ -2323,10 +2357,15 @@ void PrintObject::discover_horizontal_shells()
                     continue;
 //                Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
                 
-                size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value;                
-                for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) {
-                    if (n < 0 || n >= int(m_layers.size()))
-                        continue;
+                // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
+                for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
+                	(type == stTop) ?
+                		(n >= 0                   && (int(i) - n < region_config.top_solid_layers.value || 
+                								 	  print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) :
+                		(n < int(m_layers.size()) && (n - int(i) < region_config.bottom_solid_layers.value ||
+                									  m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON));
+                	(type == stTop) ? -- n : ++ n)
+                {
 //                    Slic3r::debugf "  looking for neighbors on layer %d...\n", $n;                  
                     // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
                     LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];
diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp
index 2a32ba5ef..82d2d1989 100644
--- a/src/libslic3r/Slicing.cpp
+++ b/src/libslic3r/Slicing.cpp
@@ -41,6 +41,23 @@ inline coordf_t max_layer_height_from_nozzle(const PrintConfig &print_config, in
     return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height);
 }
 
+// Minimum layer height for the variable layer height algorithm.
+coordf_t Slicing::min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle)
+{
+    coordf_t min_layer_height = print_config.opt_float("min_layer_height", idx_nozzle - 1);
+    return (min_layer_height == 0.) ? MIN_LAYER_HEIGHT_DEFAULT : std::max(MIN_LAYER_HEIGHT, min_layer_height);
+}
+
+// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default,
+// it should not be smaller than the minimum layer height.
+coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle)
+{
+    coordf_t min_layer_height = min_layer_height_from_nozzle(print_config, idx_nozzle);
+    coordf_t max_layer_height = print_config.opt_float("max_layer_height", idx_nozzle - 1);
+    coordf_t nozzle_dmr       = print_config.opt_float("nozzle_diameter", idx_nozzle - 1);
+    return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height);
+}
+
 SlicingParameters SlicingParameters::create_from_config(
 	const PrintConfig 		&print_config, 
 	const PrintObjectConfig &object_config,
diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp
index 036344b22..95cf6891b 100644
--- a/src/libslic3r/Slicing.hpp
+++ b/src/libslic3r/Slicing.hpp
@@ -99,7 +99,6 @@ struct SlicingParameters
 };
 static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
 
-
 // The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
 inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
 {
@@ -183,7 +182,17 @@ extern int generate_layer_height_texture(
     const std::vector<coordf_t> &layers,
     void *data, int rows, int cols, bool level_of_detail_2nd_level);
 
-}; // namespace Slic3r
+namespace Slicing {
+	// Minimum layer height for the variable layer height algorithm. Nozzle index is 1 based.
+	coordf_t min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle);
+
+	// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default,
+	// it should not be smaller than the minimum layer height.
+	// Nozzle index is 1 based.
+	coordf_t max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle);
+} // namespace Slicing
+
+} // namespace Slic3r
 
 namespace cereal
 {
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 966f34761..a8773d736 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -233,22 +233,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
                     "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" })
         toggle_field(el, have_infill);
 
-    bool have_solid_infill = config->opt_int("top_solid_layers") > 0 || config->opt_int("bottom_solid_layers") > 0;
+    bool has_spiral_vase         = config->opt_bool("spiral_vase");
+    bool has_top_solid_infill 	 = config->opt_int("top_solid_layers") > 0;
+    bool has_bottom_solid_infill = config->opt_int("bottom_solid_layers") > 0;
+    bool has_solid_infill 		 = has_top_solid_infill || has_bottom_solid_infill;
     // solid_infill_extruder uses the same logic as in Print::extruders()
     for (auto el : { "top_fill_pattern", "bottom_fill_pattern", "infill_first", "solid_infill_extruder",
                     "solid_infill_extrusion_width", "solid_infill_speed" })
-        toggle_field(el, have_solid_infill);
+        toggle_field(el, has_solid_infill);
 
     for (auto el : { "fill_angle", "bridge_angle", "infill_extrusion_width",
                     "infill_speed", "bridge_speed" })
-        toggle_field(el, have_infill || have_solid_infill);
+        toggle_field(el, have_infill || has_solid_infill);
+
+    toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill);
+    toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill);
 
     // Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
     toggle_field("gap_fill_speed", have_perimeters);
 
-    bool have_top_solid_infill = config->opt_int("top_solid_layers") > 0;
     for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
-        toggle_field(el, have_top_solid_infill);
+        toggle_field(el, has_top_solid_infill);
 
     bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
     for (auto el : { "perimeter_acceleration", "infill_acceleration",
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 1b69decfc..aadfdd0dd 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1980,7 +1980,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
         "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
         // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
         "layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
-        "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "bottom_solid_layers", "solid_infill_extruder",
+        "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", 
         "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers"
         }))
     , sidebar(new Sidebar(q))
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 98fcf3f42..00e004f75 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -386,7 +386,8 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
 const std::vector<std::string>& Preset::print_options()
 {
     static std::vector<std::string> s_opts {
-        "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers",
+        "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", 
+        "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
         "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
         "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
         "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp
index f6281d7af..71db6d35b 100644
--- a/src/slic3r/GUI/PresetHints.cpp
+++ b/src/slic3r/GUI/PresetHints.cpp
@@ -1,6 +1,7 @@
 #include <cassert>
 
 #include "libslic3r/Flow.hpp"
+#include "libslic3r/Slicing.hpp"
 #include "libslic3r/libslic3r.h"
 
 #include "PresetBundle.hpp"
@@ -242,7 +243,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
     float   nozzle_diameter                     = float(printer_config.opt_float("nozzle_diameter", 0));
     
     std::string out;
-	if (layer_height <= 0.f){
+	if (layer_height <= 0.f) {
 		out += _utf8(L("Recommended object thin wall thickness: Not available due to invalid layer height."));
 		return out;
 	}
@@ -272,4 +273,70 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
     return out;
 }
 
+
+// Produce a textual explanation of the combined effects of the top/bottom_solid_layers
+// versus top/bottom_min_shell_thickness. Which of the two values wins depends
+// on the active layer height.
+std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle)
+{
+    const DynamicPrintConfig &print_config    = preset_bundle.prints   .get_edited_preset().config;
+    const DynamicPrintConfig &printer_config  = preset_bundle.printers .get_edited_preset().config;
+
+    std::string out;
+
+    int 	top_solid_layers                = print_config.opt_int("top_solid_layers");
+    int 	bottom_solid_layers             = print_config.opt_int("bottom_solid_layers");
+    bool    has_top_layers 					= top_solid_layers > 0;
+    bool    has_bottom_layers 				= bottom_solid_layers > 0;
+    bool 	has_shell						= has_top_layers && has_bottom_layers;
+    double  top_solid_min_thickness        	= print_config.opt_float("top_solid_min_thickness");
+    double  bottom_solid_min_thickness  	= print_config.opt_float("bottom_solid_min_thickness");
+    double  layer_height                    = print_config.opt_float("layer_height");
+    bool    variable_layer_height			= printer_config.opt_bool("variable_layer_height");
+    //FIXME the following lines take into account the 1st extruder only.
+    double  min_layer_height				= (has_shell && variable_layer_height) ? Slicing::min_layer_height_from_nozzle(printer_config, 1) : layer_height;
+    double  max_layer_height				= (has_shell && variable_layer_height) ? Slicing::max_layer_height_from_nozzle(printer_config, 1) : layer_height;
+
+	if (layer_height <= 0.f) {
+		out += _utf8(L("Top / bottom shell thickness hint: Not available due to invalid layer height."));
+		return out;
+	}
+
+    if (has_top_layers) {
+    	double top_shell_thickness = top_solid_layers * layer_height;
+    	if (top_shell_thickness < top_solid_min_thickness) {
+    		// top_solid_min_shell_thickness triggers even in case of normal layer height. Round the top_shell_thickness up
+    		// to an integer multiply of layer_height.
+    		double n = ceil(top_solid_min_thickness / layer_height);
+    		top_shell_thickness = n * layer_height;
+    	}
+    	double top_shell_thickness_minimum = std::max(top_solid_min_thickness, top_solid_layers * min_layer_height);
+        out += (boost::format(_utf8(L("Top shell is %1% mm thick for layer height %2% mm."))) % top_shell_thickness % layer_height).str();
+        if (variable_layer_height && top_shell_thickness_minimum < top_shell_thickness) {
+        	out += " ";
+	        out += (boost::format(_utf8(L("Minimum top shell thickness is %1% mm."))) % top_shell_thickness_minimum).str();        	
+        }
+    }
+
+    if (has_bottom_layers) {
+    	double bottom_shell_thickness = bottom_solid_layers * layer_height;
+    	if (bottom_shell_thickness < bottom_solid_min_thickness) {
+    		// bottom_solid_min_shell_thickness triggers even in case of normal layer height. Round the bottom_shell_thickness up
+    		// to an integer multiply of layer_height.
+    		double n = ceil(bottom_solid_min_thickness / layer_height);
+    		bottom_shell_thickness = n * layer_height;
+    	}
+    	double bottom_shell_thickness_minimum = std::max(bottom_solid_min_thickness, bottom_solid_layers * min_layer_height);
+    	if (! out.empty())
+    		out += "\n";
+        out += (boost::format(_utf8(L("Bottom shell is %1% mm thick for layer height %2% mm."))) % bottom_shell_thickness % layer_height).str();
+        if (variable_layer_height && bottom_shell_thickness_minimum < bottom_shell_thickness) {
+        	out += " ";
+	        out += (boost::format(_utf8(L("Minimum bottom shell thickness is %1% mm."))) % bottom_shell_thickness_minimum).str();        	
+        }
+    }
+
+    return out;
+}
+
 }; // namespace Slic3r
diff --git a/src/slic3r/GUI/PresetHints.hpp b/src/slic3r/GUI/PresetHints.hpp
index 39bf0b100..be049c2c8 100644
--- a/src/slic3r/GUI/PresetHints.hpp
+++ b/src/slic3r/GUI/PresetHints.hpp
@@ -23,6 +23,11 @@ public:
     // Produce a textual description of a recommended thin wall thickness
     // from the provided number of perimeters and the external / internal perimeter width.
     static std::string recommended_thin_wall_thickness(const PresetBundle &preset_bundle);
+
+    // Produce a textual explanation of the combined effects of the top/bottom_solid_layers
+    // versus top/bottom_min_shell_thickness. Which of the two values wins depends
+    // on the active layer height.
+    static std::string top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle);    
 };
 
 } // namespace Slic3r
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 72e209167..5a41f12a5 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1056,6 +1056,16 @@ void TabPrint::build()
         line.append_option(optgroup->get_option("top_solid_layers"));
         line.append_option(optgroup->get_option("bottom_solid_layers"));
         optgroup->append_line(line);
+    	line = { _(L("Minimum shell thickness")), "" };
+        line.append_option(optgroup->get_option("top_solid_min_thickness"));
+        line.append_option(optgroup->get_option("bottom_solid_min_thickness"));
+        optgroup->append_line(line);
+		line = { "", "" };
+	    line.full_width = 1;
+	    line.widget = [this](wxWindow* parent) {
+	        return description_line_widget(parent, &m_top_bottom_shell_thickness_explanation);
+	    };
+	    optgroup->append_line(line);
 
         optgroup = page->new_optgroup(_(L("Quality (slower slicing)")));
         optgroup->append_single_option_line("extra_perimeters");
@@ -1277,6 +1287,8 @@ void TabPrint::update()
 
     m_recommended_thin_wall_thickness_description_line->SetText(
         from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
+    m_top_bottom_shell_thickness_explanation->SetText(
+        from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle)));
     Layout();
 
 //	Thaw();
@@ -1295,6 +1307,8 @@ void TabPrint::OnActivate()
 {
     m_recommended_thin_wall_thickness_description_line->SetText(
         from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
+    m_top_bottom_shell_thickness_explanation->SetText(
+        from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle)));
     Tab::OnActivate();
 }
 
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index cfa5ae56d..c88a74e53 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -327,8 +327,9 @@ public:
         Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {}
 	~TabPrint() {}
 
-	ogStaticText*	m_recommended_thin_wall_thickness_description_line;
-	bool		m_support_material_overhangs_queried = false;
+	ogStaticText*	m_recommended_thin_wall_thickness_description_line = nullptr;
+	ogStaticText*	m_top_bottom_shell_thickness_explanation = nullptr;
+	bool			m_support_material_overhangs_queried = false;
 
 	void		build() override;
 	void		reload_config() override;
@@ -336,6 +337,7 @@ public:
 	void		OnActivate() override;
     bool 		supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
 };
+
 class TabFilament : public Tab
 {
 	ogStaticText*	m_volumetric_speed_description_line;