diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 72cbe150f..3c0f27f4d 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -60,21 +60,25 @@ PrinterTechnology get_printer_technology(const DynamicConfig &config)
 
 int CLI::run(int argc, char **argv)
 {
-#ifdef _WIN32
 	// Switch boost::filesystem to utf8.
     try {
         boost::nowide::nowide_filesystem();
     } catch (const std::runtime_error& ex) {
         std::string caption = std::string(SLIC3R_APP_NAME) + " Error";
-        std::string text = std::string("An error occured while setting up locale.\n") + SLIC3R_APP_NAME + " will now terminate.\n\n" + ex.what();
-    #ifdef SLIC3R_GUI
+        std::string text = std::string("An error occured while setting up locale.\n") + (
+#if !defined(_WIN32) && !defined(__APPLE__)
+        	// likely some linux system
+            "You may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n"
+#endif
+        	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
+    #if defined(_WIN32) && defined(SLIC3R_GUI)
         if (m_actions.empty())
+        	// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
     #endif
         boost::nowide::cerr << text.c_str() << std::endl;
         return 1;
     }
-#endif
 
 	if (! this->setup(argc, argv))
 		return 1;
@@ -426,7 +430,7 @@ int CLI::run(int argc, char **argv)
                             outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
                             sla_print.export_raster(outfile_final);
                         }
-                        if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
+                        if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) {
                             boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
                             return 1;
                         }
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 433167e89..cc7262cd8 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -607,7 +607,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
         m_analyzer.reset();
     }
 
-    if (rename_file(path_tmp, path) != 0)
+    if (rename_file(path_tmp, path))
         throw std::runtime_error(
             std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
             "Is " + path_tmp + " locked?" + '\n');
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index 8a2e1266a..a03d3c7c8 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -390,7 +390,7 @@ namespace Slic3r {
         fclose(out);
         in.close();
 
-        if (rename_file(path_tmp, filename) != 0)
+        if (rename_file(path_tmp, filename))
             throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
             "Is " + path_tmp + " locked?" + '\n');
 
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 31de80e8b..8f56c1b83 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2412,6 +2412,22 @@ void PrintConfigDef::init_sla_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionInt(10));
 
+    def = this->add("min_exposure_time", coFloat);
+    def->label = L("Minimum exposure time");
+    def->tooltip = L("Minimum exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0));
+
+    def = this->add("max_exposure_time", coFloat);
+    def->label = L("Maximum exposure time");
+    def->tooltip = L("Maximum exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(100));
+
     def = this->add("exposure_time", coFloat);
     def->label = L("Exposure time");
     def->tooltip = L("Exposure time");
@@ -2419,6 +2435,22 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->set_default_value(new ConfigOptionFloat(10));
 
+    def = this->add("min_initial_exposure_time", coFloat);
+    def->label = L("Minimum initial exposure time");
+    def->tooltip = L("Minimum initial exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0));
+
+    def = this->add("max_initial_exposure_time", coFloat);
+    def->label = L("Maximum initial exposure time");
+    def->tooltip = L("Maximum initial exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(150));
+
     def = this->add("initial_exposure_time", coFloat);
     def->label = L("Initial exposure time");
     def->tooltip = L("Initial exposure time");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 081f670e1..35025fcd1 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1131,6 +1131,10 @@ public:
     ConfigOptionFloat                       fast_tilt_time;
     ConfigOptionFloat                       slow_tilt_time;
     ConfigOptionFloat                       area_fill;
+    ConfigOptionFloat                       min_exposure_time;
+    ConfigOptionFloat                       max_exposure_time;
+    ConfigOptionFloat                       min_initial_exposure_time;
+    ConfigOptionFloat                       max_initial_exposure_time;
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
@@ -1150,6 +1154,10 @@ protected:
         OPT_PTR(fast_tilt_time);
         OPT_PTR(slow_tilt_time);
         OPT_PTR(area_fill);
+        OPT_PTR(min_exposure_time);
+        OPT_PTR(max_exposure_time);
+        OPT_PTR(min_initial_exposure_time);
+        OPT_PTR(max_initial_exposure_time);
     }
 };
 
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 1529f4baf..21aec8384 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -692,6 +692,20 @@ std::string SLAPrint::validate() const
         }
     }
 
+    double expt_max = m_printer_config.max_exposure_time.getFloat();
+    double expt_min = m_printer_config.min_exposure_time.getFloat();
+    double expt_cur = m_material_config.exposure_time.getFloat();
+
+    if (expt_cur < expt_min || expt_cur > expt_max)
+        return L("Exposition time is out of printer profile bounds.");
+
+    double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat();
+    double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat();
+    double iexpt_cur = m_material_config.initial_exposure_time.getFloat();
+
+    if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max)
+        return L("Initial exposition time is out of printer profile bounds.");
+
     return "";
 }
 
@@ -1586,7 +1600,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
     // Cache the plenty of parameters, which influence the final rasterization only,
     // or they are only notes not influencing the rasterization step.
     static std::unordered_set<std::string> steps_rasterize = {
+        "min_exposure_time",
+        "max_exposure_time",
         "exposure_time",
+        "min_initial_exposure_time",
+        "max_initial_exposure_time",
         "initial_exposure_time",
         "display_width",
         "display_height",
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index b19027826..2b1fdb241 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -61,7 +61,7 @@ extern std::string normalize_utf8_nfc(const char *src);
 // Safely rename a file even if the target exists.
 // On Windows, the file explorer (or anti-virus or whatever else) often locks the file
 // for a short while, so the file may not be movable. Retry while we see recoverable errors.
-extern int rename_file(const std::string &from, const std::string &to);
+extern std::error_code rename_file(const std::string &from, const std::string &to);
 
 // Copy a file, adjust the access attributes, so that the target is writable.
 extern int copy_file(const std::string &from, const std::string &to);
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index 8fcd611ac..e26ed3839 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -173,67 +173,247 @@ const std::string& data_dir()
     return g_data_dir;
 }
 
+#ifdef _WIN32
+// The following helpers are borrowed from the LLVM project https://github.com/llvm
+namespace WindowsSupport
+{
+	template <typename HandleTraits>
+	class ScopedHandle {
+		typedef typename HandleTraits::handle_type handle_type;
+		handle_type Handle;
+		ScopedHandle(const ScopedHandle &other) = delete;
+		void operator=(const ScopedHandle &other) = delete;
+	public:
+		ScopedHandle() : Handle(HandleTraits::GetInvalid()) {}
+	  	explicit ScopedHandle(handle_type h) : Handle(h) {}
+	  	~ScopedHandle() { if (HandleTraits::IsValid(Handle)) HandleTraits::Close(Handle); }
+	  	handle_type take() {
+	    	handle_type t = Handle;
+	    	Handle = HandleTraits::GetInvalid();
+	    	return t;
+	  	}
+	  	ScopedHandle &operator=(handle_type h) {
+	    	if (HandleTraits::IsValid(Handle))
+	      		HandleTraits::Close(Handle);
+	    	Handle = h;
+	    	return *this;
+	  	}
+	  	// True if Handle is valid.
+	  	explicit operator bool() const { return HandleTraits::IsValid(Handle) ? true : false; }
+	  	operator handle_type() const { return Handle; }
+	};
+
+	struct CommonHandleTraits {
+	  	typedef HANDLE handle_type;
+	  	static handle_type GetInvalid() { return INVALID_HANDLE_VALUE; }
+	  	static void Close(handle_type h) { ::CloseHandle(h); }
+	  	static bool IsValid(handle_type h) { return h != GetInvalid(); }
+	};
+
+	typedef ScopedHandle<CommonHandleTraits> ScopedFileHandle;
+
+	std::error_code map_windows_error(unsigned windows_error_code)
+	{
+		#define MAP_ERR_TO_COND(x, y) case x: return std::make_error_code(std::errc::y)
+		switch (windows_error_code) {
+			MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
+			MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
+			MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
+			MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
+			MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
+			MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
+			MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
+			MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
+			MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
+			MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
+			MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
+			MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
+			MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
+			MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
+			MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
+			MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
+			MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
+			MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
+			MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
+			MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
+			MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
+			MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
+			MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
+			MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
+			MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
+			MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
+			MAP_ERR_TO_COND(ERROR_SEEK, io_error);
+			MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
+			MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
+			MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
+			MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
+			MAP_ERR_TO_COND(WSAEACCES, permission_denied);
+			MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
+			MAP_ERR_TO_COND(WSAEFAULT, bad_address);
+			MAP_ERR_TO_COND(WSAEINTR, interrupted);
+			MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
+			MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
+			MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
+		default:
+			return std::error_code(windows_error_code, std::system_category());
+		}
+		#undef MAP_ERR_TO_COND
+	}
+
+	static std::error_code rename_internal(HANDLE from_handle, const std::wstring &wide_to, bool replace_if_exists)
+	{
+		std::vector<char> rename_info_buf(sizeof(FILE_RENAME_INFO) - sizeof(wchar_t) + (wide_to.size() * sizeof(wchar_t)));
+		FILE_RENAME_INFO &rename_info = *reinterpret_cast<FILE_RENAME_INFO*>(rename_info_buf.data());
+		rename_info.ReplaceIfExists = replace_if_exists;
+		rename_info.RootDirectory = 0;
+		rename_info.FileNameLength = DWORD(wide_to.size() * sizeof(wchar_t));
+		std::copy(wide_to.begin(), wide_to.end(), &rename_info.FileName[0]);
+
+		::SetLastError(ERROR_SUCCESS);
+		if (! ::SetFileInformationByHandle(from_handle, FileRenameInfo, &rename_info, (DWORD)rename_info_buf.size())) {
+			unsigned Error = GetLastError();
+			if (Error == ERROR_SUCCESS)
+		  		Error = ERROR_CALL_NOT_IMPLEMENTED; // Wine doesn't always set error code.
+			return map_windows_error(Error);
+		}
+
+		return std::error_code();
+	}
+
+	static std::error_code real_path_from_handle(HANDLE H, std::wstring &buffer)
+	{
+		buffer.resize(MAX_PATH + 1);
+		DWORD CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
+	  	if (CountChars > buffer.size()) {
+	    	// The buffer wasn't big enough, try again.  In this case the return value *does* indicate the size of the null terminator.
+	    	buffer.resize((size_t)CountChars);
+	    	CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
+	  	}
+	  	if (CountChars == 0)
+	    	return map_windows_error(GetLastError());
+	  	buffer.resize(CountChars);
+	  	return std::error_code();
+	}
+
+	std::error_code rename(const std::string &from, const std::string &to)
+	{
+		// Convert to utf-16.
+		std::wstring wide_from = boost::nowide::widen(from);
+		std::wstring wide_to   = boost::nowide::widen(to);
+
+		ScopedFileHandle from_handle;
+		// Retry this a few times to defeat badly behaved file system scanners.
+		for (unsigned retry = 0; retry != 200; ++ retry) {
+			if (retry != 0)
+		  		::Sleep(10);
+			from_handle = ::CreateFileW((LPWSTR)wide_from.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+			if (from_handle)
+		  		break;
+		}
+		if (! from_handle)
+			return map_windows_error(GetLastError());
+
+		// We normally expect this loop to succeed after a few iterations. If it
+		// requires more than 200 tries, it's more likely that the failures are due to
+		// a true error, so stop trying.
+		for (unsigned retry = 0; retry != 200; ++ retry) {
+			auto errcode = rename_internal(from_handle, wide_to, true);
+
+			if (errcode == std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) {
+		  		// Wine doesn't support SetFileInformationByHandle in rename_internal.
+		  		// Fall back to MoveFileEx.
+		  		if (std::error_code errcode2 = real_path_from_handle(from_handle, wide_from))
+		    		return errcode2;
+		  		if (::MoveFileExW((LPCWSTR)wide_from.data(), (LPCWSTR)wide_to.data(), MOVEFILE_REPLACE_EXISTING))
+		    		return std::error_code();
+		  		return map_windows_error(GetLastError());
+			}
+
+			if (! errcode || errcode != std::errc::permission_denied)
+		  		return errcode;
+
+			// The destination file probably exists and is currently open in another
+			// process, either because the file was opened without FILE_SHARE_DELETE or
+			// it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to
+			// move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE
+			// to arrange for the destination file to be deleted when the other process
+			// closes it.
+			ScopedFileHandle to_handle(::CreateFileW((LPCWSTR)wide_to.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL));
+			if (! to_handle) {
+				auto errcode = map_windows_error(GetLastError());
+				// Another process might have raced with us and moved the existing file
+				// out of the way before we had a chance to open it. If that happens, try
+				// to rename the source file again.
+				if (errcode == std::errc::no_such_file_or_directory)
+					continue;
+				return errcode;
+			}
+
+			BY_HANDLE_FILE_INFORMATION FI;
+			if (! ::GetFileInformationByHandle(to_handle, &FI))
+		  		return map_windows_error(GetLastError());
+
+			// Try to find a unique new name for the destination file.
+			for (unsigned unique_id = 0; unique_id != 200; ++ unique_id) {
+				std::wstring tmp_filename = wide_to + L".tmp" + std::to_wstring(unique_id);
+				std::error_code errcode = rename_internal(to_handle, tmp_filename, false);
+				if (errcode) {
+					if (errcode == std::make_error_code(std::errc::file_exists) || errcode == std::make_error_code(std::errc::permission_denied)) {
+						// Again, another process might have raced with us and moved the file
+						// before we could move it. Check whether this is the case, as it
+						// might have caused the permission denied error. If that was the
+						// case, we don't need to move it ourselves.
+						ScopedFileHandle to_handle2(::CreateFileW((LPCWSTR)wide_to.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+						if (! to_handle2) {
+							auto errcode = map_windows_error(GetLastError());
+							if (errcode == std::errc::no_such_file_or_directory)
+						  		break;
+							return errcode;
+						}
+						BY_HANDLE_FILE_INFORMATION FI2;
+						if (! ::GetFileInformationByHandle(to_handle2, &FI2))
+							return map_windows_error(GetLastError());
+						if (FI.nFileIndexHigh != FI2.nFileIndexHigh || FI.nFileIndexLow != FI2.nFileIndexLow || FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber)
+							break;
+						continue;
+					}
+					return errcode;
+				}
+				break;
+			}
+
+			// Okay, the old destination file has probably been moved out of the way at
+			// this point, so try to rename the source file again. Still, another
+			// process might have raced with us to create and open the destination
+			// file, so we need to keep doing this until we succeed.
+		}
+
+		// The most likely root cause.
+		return std::make_error_code(std::errc::permission_denied);
+	}
+} // namespace WindowsSupport
+#endif /* _WIN32 */
 
 // borrowed from LVVM lib/Support/Windows/Path.inc
-int rename_file(const std::string &from, const std::string &to)
+std::error_code rename_file(const std::string &from, const std::string &to)
 {
-    int ec = 0;
-
 #ifdef _WIN32
-
-	// Convert to utf-16.
-    std::wstring wide_from = boost::nowide::widen(from);
-    std::wstring wide_to   = boost::nowide::widen(to);
-
-    // Retry while we see recoverable errors.
-    // System scanners (eg. indexer) might open the source file when it is written
-    // and closed.
-    bool TryReplace = true;
-
-    // This loop may take more than 2000 x 1ms to finish.
-    for (int i = 0; i < 2000; ++ i) {
-        if (i > 0)
-            // Sleep 1ms
-            ::Sleep(1);
-        if (TryReplace) {
-            // Try ReplaceFile first, as it is able to associate a new data stream
-            // with the destination even if the destination file is currently open.
-            if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
-                return 0;
-            DWORD ReplaceError = ::GetLastError();
-            ec = -1; // ReplaceError
-            // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
-            // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
-            if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
-                ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
-                TryReplace = false;
-                continue;
-            }
-            // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
-            // using ReplaceFileW().
-            if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
-                continue;
-            // We get ERROR_FILE_NOT_FOUND if the destination file is missing.
-            // MoveFileEx can handle this case.
-            if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
-                break;
-        }
-        if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
-            return 0;
-        DWORD MoveError = ::GetLastError();
-        ec = -1; // MoveError
-        if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
-            break;
-    }
-
+	return WindowsSupport::rename(from, to);
 #else
-
 	boost::nowide::remove(to.c_str());
-	ec = boost::nowide::rename(from.c_str(), to.c_str());
-
+	return std::make_error_code(static_cast<std::errc>(boost::nowide::rename(from.c_str(), to.c_str())));
 #endif
-
-    return ec;
 }
 
 int copy_file(const std::string &from, const std::string &to)
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index 7ca8c3e5e..96213447c 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -98,9 +98,10 @@ void AppConfig::load()
         pt::read_ini(ifs, tree);
     } catch (pt::ptree_error& ex) {
         // Error while parsing config file. We'll customize the error message and rethrow to be displayed.
-        throw std::runtime_error(wxString::Format(_(L("Error parsing config file, it is probably corrupted. "
-            "Try to manualy delete the file. Your user profiles will not be affected.\n\n%s\n\n%s")),
-            AppConfig::config_path(), ex.what()).ToStdString());
+        throw std::runtime_error(
+        	_utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. "
+                    "Try to manualy delete the file to recover from the error. Your user profiles will not be affected.")) + 
+        	"\n\n" + AppConfig::config_path() + "\n\n" + ex.what());
     }
 
     // 2) Parse the property_tree, extract the sections and key / value pairs.
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 9b4522356..dff9fc1a9 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -293,6 +293,20 @@ bool GUI_App::on_init_inner()
                 config_wizard_startup(app_conf_exists);
                 preset_updater->slic3r_update_notify();
                 preset_updater->sync(preset_bundle);
+                const GLCanvas3DManager::GLInfo &glinfo = GLCanvas3DManager::get_gl_info();
+                if (! glinfo.is_version_greater_or_equal_to(2, 0)) {
+                	// Complain about the OpenGL version.
+                	wxString message = wxString::Format(
+                		_(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
+                			"while OpenGL version %s, render %s, vendor %s was detected.")), wxString(glinfo.get_version()), wxString(glinfo.get_renderer()), wxString(glinfo.get_vendor()));
+                	message += "\n";
+                	message += _(L("You may need to update your graphics card driver."));
+#ifdef _WIN32
+                	message += "\n";
+                	message += _(L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."));
+#endif
+                	wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR);
+                }
             });
         }
     });
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index 2bbf11170..29013389e 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -153,8 +153,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
 
     auto manifold_warning_icon = [this](wxWindow* parent) {
         m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
-        auto sizer = new wxBoxSizer(wxHORIZONTAL);
-        sizer->Add(m_fix_throught_netfab_bitmap);
+//         auto sizer = new wxBoxSizer(wxHORIZONTAL);
+//         sizer->Add(m_fix_throught_netfab_bitmap);
 
         if (is_windows10())
             m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
@@ -167,17 +167,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
                 update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
             });
 
-        return sizer;
+//         return sizer;
+        return m_fix_throught_netfab_bitmap;
     };
 
-    line.append_widget(manifold_warning_icon);
+ //   line.append_widget(manifold_warning_icon);
+    line.near_label_widget = manifold_warning_icon;
     def.label = "";
     def.gui_type = "legend";
     def.tooltip = L("Object name");
 #ifdef __APPLE__
-    def.width = 19;
+    def.width = 20;
 #else
-    def.width = 21;
+    def.width = 22;
 #endif
     def.set_default_value(new ConfigOptionString{ " " });
     line.append_option(Option(def, "object_name"));
@@ -392,10 +394,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
 
     // call back for a rescale of button "Set uniform scale"
     m_og->rescale_near_label_widget = [this](wxWindow* win) {
+        // rescale lock icon
         auto *ctrl = dynamic_cast<LockButton*>(win);
-        if (ctrl == nullptr)
+        if (ctrl != nullptr) {
+            ctrl->msw_rescale();
             return;
-        ctrl->msw_rescale();
+        }
+
+        if (win == m_fix_throught_netfab_bitmap)
+            return;
+
+        // rescale "place" of the empty icon (to correct layout of the "Size" and "Scale")
+        if (dynamic_cast<wxStaticBitmap*>(win) != nullptr)
+            win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
     };
 }
  
@@ -685,6 +696,7 @@ void ObjectManipulation::emulate_kill_focus()
 void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
 {
     m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
+    m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
     m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
 }
 
@@ -919,7 +931,10 @@ void ObjectManipulation::msw_rescale()
 {
     msw_rescale_word_local_combo(m_word_local_combo);
     m_manifold_warning_bmp.msw_rescale();
-    m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
+
+    const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText();
+    m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
+    m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize());
 
     m_mirror_bitmap_on.msw_rescale();
     m_mirror_bitmap_off.msw_rescale();
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index a17bbf6d3..c47714e46 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -64,6 +64,12 @@ public:
         m_prev_scale_factor = m_scale_factor;
 		m_normal_font = get_default_font_for_dpi(dpi);
 
+        /* Because of default window font is a primary display font, 
+         * We should set correct font for window before getting em_unit value.
+         */
+#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList 
+        this->SetFont(m_normal_font);
+#endif
         // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
         m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
 
@@ -72,6 +78,8 @@ public:
         this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
             m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
 
+            m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
+
             if (!m_can_rescale)
                 return;
 
@@ -124,6 +132,8 @@ private:
     float m_prev_scale_factor;
     bool  m_can_rescale{ true };
 
+    int   m_new_font_point_size;
+
 //    void recalc_font()
 //    {
 //        wxClientDC dc(this);
@@ -135,14 +145,22 @@ private:
     // check if new scale is differ from previous
     bool    is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
 
+    // function for a font scaling of the window
+    void    scale_win_font(wxWindow *window, const int font_point_size)
+    {
+        wxFont new_font(window->GetFont());
+        new_font.SetPointSize(font_point_size);
+        window->SetFont(new_font);
+    }
+
     // recursive function for scaling fonts for all controls in Window
-    void    scale_controls_fonts(wxWindow *window, const float scale_f)
+    void    scale_controls_fonts(wxWindow *window, const int font_point_size)
     {
         auto children = window->GetChildren();
 
         for (auto child : children) {
-            scale_controls_fonts(child, scale_f);
-            child->SetFont(child->GetFont().Scaled(scale_f));
+            scale_controls_fonts(child, font_point_size);
+            scale_win_font(child, font_point_size);
         }
 
         window->Layout();
@@ -151,18 +169,18 @@ private:
     void    rescale(const wxRect &suggested_rect)
     {
         this->Freeze();
-        const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
 
         // rescale fonts of all controls
-        scale_controls_fonts(this, relative_scale_factor);
-        this->SetFont(this->GetFont().Scaled(relative_scale_factor));
+        scale_controls_fonts(this, m_new_font_point_size);
+        // rescale current window font
+        scale_win_font(this, m_new_font_point_size);
 
 
-        // rescale normal_font value
-        m_normal_font = m_normal_font.Scaled(relative_scale_factor);
+        // set normal application font as a current window font
+        m_normal_font = this->GetFont();
 
-        // An analog of em_unit value from GUI_App.
-        m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
+        // update em_unit value for new window font
+        m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
 
         // rescale missed controls sizes and images
         on_dpi_changed(suggested_rect);
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index f10a106c1..12a38d2fc 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -39,10 +39,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
 {
     // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
     wxGetApp().update_fonts(this);
+/*
 #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList 
     this->SetFont(this->normal_font());
 #endif
-
+    // Font is already set in DPIFrame constructor
+*/
     // Load the icon either from the exe, or from the ico file.
 #if _WIN32
     {
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 833da238a..64793630c 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -500,7 +500,8 @@ const std::vector<std::string>& Preset::sla_material_options()
     if (s_opts.empty()) {
         s_opts = {
             "initial_layer_height",
-            "exposure_time", "initial_exposure_time",
+            "exposure_time",
+            "initial_exposure_time",
             "material_correction",
             "material_notes",
             "default_sla_material_profile",
@@ -526,6 +527,8 @@ const std::vector<std::string>& Preset::sla_printer_options()
             "relative_correction",
             "absolute_correction",
             "gamma_correction",
+            "min_exposure_time", "max_exposure_time",
+            "min_initial_exposure_time", "max_initial_exposure_time",
             "print_host", "printhost_apikey", "printhost_cafile",
             "printer_notes",
             "inherits"
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 4afd3a116..368854222 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -838,7 +838,7 @@ static wxString support_combo_value_for_config(const DynamicPrintConfig &config,
 
 static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
 {
-	return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
+    return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
 }
 
 void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
@@ -860,8 +860,8 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
             (opt_key == "supports_enable"  || opt_key == "support_buildplate_only"))
         og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
 
-	if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
-		og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
+    if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
+        og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
 
     if (opt_key == "brim_width")
     {
@@ -998,7 +998,7 @@ void Tab::update_frequently_changed_parameters()
 
     og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
     if (! is_fff)
-    	og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
+        og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
 
     const std::string updated_value_key = is_fff ? "fill_density" : "pad_enable";
 
@@ -1772,13 +1772,13 @@ void TabFilament::reload_config()
 
 void TabFilament::update_volumetric_flow_preset_hints()
 {
-	wxString text;
-	try {
-		text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
-	} catch (std::exception &ex) {
-		text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
-	}
-	m_volumetric_speed_description_line->SetText(text);
+    wxString text;
+    try {
+        text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
+    } catch (std::exception &ex) {
+        text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
+    }
+    m_volumetric_speed_description_line->SetText(text);
 }
 
 void TabFilament::update()
@@ -1788,9 +1788,9 @@ void TabFilament::update()
 
     m_update_cnt++;
 
-	wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
-	m_cooling_description_line->SetText(text);
-	this->update_volumetric_flow_preset_hints();
+    wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
+    m_cooling_description_line->SetText(text);
+    this->update_volumetric_flow_preset_hints();
     Layout();
 
     bool cooling = m_config->opt_bool("cooling", 0);
@@ -1812,8 +1812,8 @@ void TabFilament::update()
 
 void TabFilament::OnActivate()
 {
-	this->update_volumetric_flow_preset_hints();
-	Tab::OnActivate();
+    this->update_volumetric_flow_preset_hints();
+    Tab::OnActivate();
 }
 
 wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText)
@@ -2290,6 +2290,12 @@ void TabPrinter::build_sla()
     optgroup->append_single_option_line("absolute_correction");
     optgroup->append_single_option_line("gamma_correction");
 
+    optgroup = page->new_optgroup(_(L("Exposure")));
+    optgroup->append_single_option_line("min_exposure_time");
+    optgroup->append_single_option_line("max_exposure_time");
+    optgroup->append_single_option_line("min_initial_exposure_time");
+    optgroup->append_single_option_line("max_initial_exposure_time");
+
     optgroup = page->new_optgroup(_(L("Print Host upload")));
     build_printhost(optgroup.get());
 
@@ -2560,7 +2566,7 @@ void TabPrinter::build_unregular_pages()
             optgroup = page->new_optgroup(_(L("Preview")));
 
             auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) {
-                add_scaled_button(parent, &m_reset_to_filament_color, "undo", 
+                add_scaled_button(parent, &m_reset_to_filament_color, "undo",
                                   _(L("Reset to Filament Color")), wxBU_LEFT | wxBU_EXACTFIT);
                 ScalableButton* btn = m_reset_to_filament_color;
                 btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@@ -2571,7 +2577,7 @@ void TabPrinter::build_unregular_pages()
                 {
                     std::vector<std::string> colors = static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values;
                     colors[extruder_idx] = "";
-                        
+
                     DynamicPrintConfig new_conf = *m_config;
                     new_conf.set_key_value("extruder_colour", new ConfigOptionStrings(colors));
                     load_config(new_conf);