start using jobs

This commit is contained in:
Filip Sykala 2021-12-14 19:13:33 +01:00
parent 5464b650d1
commit 27bae18aab
7 changed files with 60 additions and 325 deletions

View File

@ -195,8 +195,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/NotificationProgressIndicator.cpp GUI/Jobs/NotificationProgressIndicator.cpp
GUI/Jobs/ThreadSafeQueue.hpp GUI/Jobs/ThreadSafeQueue.hpp
GUI/Jobs/SLAImportDialog.hpp GUI/Jobs/SLAImportDialog.hpp
GUI/Jobs/ReRunJob.hpp
GUI/Jobs/StopableJob.hpp
GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.hpp
GUI/ProgressStatusBar.cpp GUI/ProgressStatusBar.cpp
GUI/Mouse3DController.cpp GUI/Mouse3DController.cpp

View File

@ -201,7 +201,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
, m_volume(nullptr) , m_volume(nullptr)
, m_exist_notification(false) , m_exist_notification(false)
, m_is_initialized(false) // initialize on first opening gizmo , m_is_initialized(false) // initialize on first opening gizmo
, m_job(std::make_unique<EmbossJob>())
, m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) , m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z)
{ {
m_rotate_gizmo.set_group_id(0); m_rotate_gizmo.set_group_id(0);
@ -582,7 +581,9 @@ bool GLGizmoEmboss::process()
if (m_font == nullptr) return false; if (m_font == nullptr) return false;
auto data = std::make_unique<EmbossData>( auto data = std::make_unique<EmbossData>(
m_font, create_configuration(), create_volume_name(), m_volume); m_font, create_configuration(), create_volume_name(), m_volume);
m_job->run(std::move(data));
auto &worker = wxGetApp().plater()->get_ui_job_worker();
replace_job(worker, std::make_unique<EmbossJob>(std::move(data)));
// notification is removed befor object is changed by job // notification is removed befor object is changed by job
remove_notification_not_valid_font(); remove_notification_not_valid_font();
@ -593,8 +594,6 @@ void GLGizmoEmboss::close()
{ {
// close gizmo == open it again // close gizmo == open it again
m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss); m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss);
m_job->stop();
m_job->join(); // free thread resource
} }
void GLGizmoEmboss::draw_window() void GLGizmoEmboss::draw_window()
@ -1610,7 +1609,7 @@ std::string GLGizmoEmboss::imgui_trunc(const std::string &text, float width)
return text.substr(0, count_letter) + tail; return text.substr(0, count_letter) + tail;
} }
namespace Slic3r { namespace Slic3r::GUI {
class Priv class Priv
{ {
@ -1645,23 +1644,8 @@ public:
, object_idx(object_idx) , object_idx(object_idx)
{} {}
}; };
struct UpdateVolume
{
TriangleMesh mesh;
std::string name;
TextConfiguration cfg;
ModelVolume * volume;
UpdateVolume(TriangleMesh && mesh,
std::string name,
TextConfiguration cfg,
ModelVolume * volume)
: mesh(std::move(mesh)), name(name), cfg(cfg), volume(volume)
{}
};
static void create_emboss_object(EmbossObject &data); static void create_emboss_object(EmbossObject &data);
static void create_emboss_volume(EmbossVolume &data); static void create_emboss_volume(EmbossVolume &data);
static void update_emboss_volume(UpdateVolume &data);
}; };
} // namespace Slic3r } // namespace Slic3r
@ -1696,21 +1680,6 @@ void GLGizmoEmboss::create_emboss_volume(TriangleMesh && mesh,
}); });
} }
void GLGizmoEmboss::update_emboss_volume(TriangleMesh && mesh,
std::string name,
TextConfiguration cfg,
ModelVolume * volume)
{
assert(volume != nullptr);
// Move data to call after is not working
// data are owen by lambda
auto data = new Priv::UpdateVolume(std::move(mesh), name, cfg, volume);
wxGetApp().plater()->CallAfter([data]() {
ScopeGuard sg([data]() { delete data; });
Priv::update_emboss_volume(*data);
});
}
void Priv::create_emboss_object(EmbossObject &data) void Priv::create_emboss_object(EmbossObject &data)
{ {
GUI_App & app = wxGetApp(); GUI_App & app = wxGetApp();
@ -1791,7 +1760,14 @@ void Priv::create_emboss_volume(EmbossVolume &data)
canvas->reload_scene(true); canvas->reload_scene(true);
} }
void Priv::update_emboss_volume(UpdateVolume &data) { void GLGizmoEmboss::update_emboss_volume(TriangleMesh && mesh,
const std::string& name,
const TextConfiguration& cfg,
ModelVolume * volume)
{
// for sure that some object is created from shape
if (mesh.its.indices.empty()) return;
GUI_App & app = wxGetApp(); // may be move to input GUI_App & app = wxGetApp(); // may be move to input
Plater * plater = app.plater(); Plater * plater = app.plater();
ObjectList * obj_list = app.obj_list(); ObjectList * obj_list = app.obj_list();
@ -1801,38 +1777,35 @@ void Priv::update_emboss_volume(UpdateVolume &data) {
// Check emboss gizmo is still open // Check emboss gizmo is still open
if (manager.get_current_type() != GLGizmosManager::Emboss) return; if (manager.get_current_type() != GLGizmosManager::Emboss) return;
const std::string &name = data.name;
plater->take_snapshot(_L("Emboss text") + ": " + name); plater->take_snapshot(_L("Emboss text") + ": " + name);
// find volume by object id - NOT WORK // find volume by object id - NOT WORK
// -> edit text change volume id so could apper not found volume // -> edit text change volume id so could apper not found volume
//ModelVolume *volume = nullptr; // ModelVolume *volume = nullptr;
//Model &model = plater->model(); // Model &model = plater->model();
//for (auto obj : model.objects) // for (auto obj : model.objects)
// for (auto vol : obj->volumes) // for (auto vol : obj->volumes)
// if (vol->id() == volume_id) { // if (vol->id() == volume_id) {
// volume = vol; // volume = vol;
// break; // break;
// } // }
//if (volume == nullptr) return; // if (volume == nullptr) return;
ModelVolume *volume = data.volume;
assert(volume != nullptr); assert(volume != nullptr);
// update volume // update volume
volume->set_mesh(std::move(data.mesh)); volume->set_mesh(std::move(mesh));
volume->set_new_unique_id(); volume->set_new_unique_id();
volume->calculate_convex_hull(); volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box(); volume->get_object()->invalidate_bounding_box();
volume->name = name; volume->name = name;
volume->text_configuration = data.cfg; volume->text_configuration = cfg;
// update volume in right panel( volume / object name) // update volume in right panel( volume / object name)
const Selection &selection = canvas->get_selection(); const Selection &selection = canvas->get_selection();
const GLVolume * v = selection.get_volume( const GLVolume * gl_volume = selection.get_volume(
*selection.get_volume_idxs().begin()); *selection.get_volume_idxs().begin());
int object_idx = v->object_idx(); int object_idx = gl_volume->object_idx();
int volume_idx = v->volume_idx(); int volume_idx = gl_volume->volume_idx();
obj_list->update_name_in_list(object_idx, volume_idx); obj_list->update_name_in_list(object_idx, volume_idx);
// update printable state on canvas // update printable state on canvas
@ -1847,7 +1820,6 @@ void Priv::update_emboss_volume(UpdateVolume &data) {
/// <summary> /// <summary>
/// WxFontUtils - Start definition /// WxFontUtils - Start definition
/// </summary> /// </summary>
std::optional<Emboss::Font> WxFontUtils::load_font(const wxFont &font) std::optional<Emboss::Font> WxFontUtils::load_font(const wxFont &font)
{ {
if (!font.IsOk()) return {}; if (!font.IsOk()) return {};

View File

@ -132,9 +132,6 @@ private:
FontProp m_font_prop; FontProp m_font_prop;
TriangleMesh m_default_mesh; // when add new text this shape is used TriangleMesh m_default_mesh; // when add new text this shape is used
// thread to process object on change text
std::unique_ptr<EmbossJob> m_job;
// actual volume // actual volume
ModelVolume *m_volume; ModelVolume *m_volume;
@ -181,8 +178,8 @@ private:
ModelVolumeType type, ModelVolumeType type,
size_t object_idx); size_t object_idx);
static void update_emboss_volume(TriangleMesh && mesh, static void update_emboss_volume(TriangleMesh && mesh,
std::string name, const std::string & name,
TextConfiguration cfg, const TextConfiguration &cfg,
ModelVolume * volume); ModelVolume * volume);
// only temporary solution // only temporary solution

View File

@ -14,46 +14,51 @@
using namespace Slic3r; using namespace Slic3r;
using namespace GUI; using namespace GUI;
void EmbossJob::process(std::unique_ptr<EmbossData> input) void EmbossJob::process(Ctl &ctl) {
{
// Changing cursor to busy // Changing cursor to busy
wxBeginBusyCursor(); wxBeginBusyCursor();
ScopeGuard sg([]() { wxEndBusyCursor(); }); ScopeGuard sg([]() { wxEndBusyCursor(); });
// only for sure // only for sure
assert(input != nullptr); assert(m_input != nullptr);
// check if exist valid font // check if exist valid font
if (input->font == nullptr) return; if (m_input->font == nullptr) return;
const TextConfiguration &cfg = input->text_configuration; const TextConfiguration &cfg = m_input->text_configuration;
const std::string & text = cfg.text; const std::string & text = cfg.text;
// Do NOT process empty string // Do NOT process empty string
if (text.empty()) return; if (text.empty()) return;
const FontProp &prop = cfg.font_prop; const FontProp &prop = cfg.font_prop;
ExPolygons shapes = Emboss::text2shapes(*input->font, text.c_str(), prop); ExPolygons shapes = Emboss::text2shapes(*m_input->font, text.c_str(), prop);
if (is_stoping()) return; if (ctl.was_canceled()) return;
// exist 2d shape made by text ? // exist 2d shape made by text ?
// (no shape means that font hasn't any of text symbols) // (no shape means that font hasn't any of text symbols)
if (shapes.empty()) return; if (shapes.empty()) return;
float scale = prop.size_in_mm / input->font->ascent; float scale = prop.size_in_mm / m_input->font->ascent;
auto projectZ = std::make_unique<Emboss::ProjectZ>(prop.emboss / scale); auto projectZ = std::make_unique<Emboss::ProjectZ>(prop.emboss / scale);
Emboss::ProjectScale project(std::move(projectZ), scale); Emboss::ProjectScale project(std::move(projectZ), scale);
TriangleMesh result(Emboss::polygons2model(shapes, project)); m_result = TriangleMesh(Emboss::polygons2model(shapes, project));
// for sure that some object is created from shape if (ctl.was_canceled()) return;
if (result.its.indices.empty()) return;
// center triangle mesh // center triangle mesh
Vec3d shift = result.bounding_box().center(); Vec3d shift = m_result.bounding_box().center();
result.translate(-shift.cast<float>()); m_result.translate(-shift.cast<float>());
}
if (is_stoping()) return;
GLGizmoEmboss::update_emboss_volume(std::move(result), input->volume_name, void EmbossJob::finalize(bool canceled, std::exception_ptr &) {
input->text_configuration, if (canceled) return;
input->volume);
// for sure that some object is created from shape
if (m_result.its.indices.empty()) return;
GLGizmoEmboss::update_emboss_volume(std::move(m_result),
m_input->volume_name,
m_input->text_configuration,
m_input->volume);
} }

View File

@ -1,12 +1,12 @@
#ifndef slic3r_EmbossJob_hpp_ #ifndef slic3r_EmbossJob_hpp_
#define slic3r_EmbossJob_hpp_ #define slic3r_EmbossJob_hpp_
#include "StopableJob.hpp"
#include "libslic3r/Emboss.hpp" #include "libslic3r/Emboss.hpp"
//#include "libslic3r/ObjectID.hpp" #include "Job.hpp"
namespace Slic3r { namespace Slic3r {
class ModelVolume; class ModelVolume;
class TriangleMesh;
} }
namespace Slic3r::GUI { namespace Slic3r::GUI {
@ -39,10 +39,14 @@ struct EmbossData
{} {}
}; };
class EmbossJob : public StopableJob<EmbossData> class EmbossJob : public Job
{ {
protected: std::unique_ptr<EmbossData> m_input;
void process(std::unique_ptr<EmbossData> input) override; TriangleMesh m_result;
public:
EmbossJob(std::unique_ptr<EmbossData> input) : m_input(std::move(input)) {}
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
}; };
} // namespace Slic3r::GUI } // namespace Slic3r::GUI

View File

@ -1,83 +0,0 @@
#ifndef slic3r_ReRunJob_hpp_
#define slic3r_ReRunJob_hpp_
#include <memory>
#include <atomic>
#include <mutex>
#include <thread>
namespace Slic3r::GUI {
// inspired by Job.hpp
// All public function can be call only from UI thread
// Mechanism to stack processing unique input and do only the last one
template<typename TIn>
class ReRunJob
{
std::mutex m_next_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// indicate checking of new input.
std::atomic<bool> m_running{false};
using Func = std::function<void(std::unique_ptr<TIn>)>;
Func m_func;
public:
ReRunJob(Func func) : m_func(func) {}
virtual ~ReRunJob() {
try {
// thread join could throw exception
// https://en.cppreference.com/w/cpp/thread/thread/join
join();
} catch (std::system_error err) {}
}
void re_run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_next_mutex);
if (is_running()) {
// when runnig
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running.store(true);
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
m_func(std::move(input));
std::lock_guard lg(m_next_mutex);
// it is not in while condition because of lock guard
if (m_input_next == nullptr) {
m_running.store(false);
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
bool is_running() const { return m_running.load(); }
void join()
{
if (m_thread.joinable()) m_thread.join();
assert(!is_running());
}
};
} // namespace Slic3r::GUI
#endif // slic3r_ReRunJob_hpp_

View File

@ -1,158 +0,0 @@
#ifndef slic3r_StopableJob_hpp_
#define slic3r_StopableJob_hpp_
#include <memory>
#include <thread>
#include <mutex>
#include <assert.h>
namespace Slic3r::GUI {
// inspired by Job.hpp
// Mechanism to stack processing and do only last one
// Ability to stop processing developer must add check is_stopping() into process()
template<typename TIn> class StopableJob
{
std::mutex m_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// when running == true, than check of new input in thread.
bool m_running = false;
// faster interupt inside func, developer must add StopCondifion call
bool m_stop = false;
public:
/// <summary>
/// Stop and join thread
/// </summary>
virtual ~StopableJob();
/// <summary>
/// Restart processing of input
/// </summary>
/// <param name="input">Data needed to process</param>
void run(std::unique_ptr<TIn> input);
/// <summary>
/// Check if job processing input now
/// </summary>
/// <returns>True when run now otherwise False</returns>
bool is_running(); // const; -- mutex
/// <summary>
/// Check if actual job is stopping now
/// </summary>
/// <returns>True when at stop process otherwise False</returns>
bool is_stoping(); // const; -- mutex
/// <summary>
/// set flag to stop processing
/// </summary>
void stop();
/// <summary>
/// Free thread resources by join thread
/// Be Carefull, it is blocking until join
/// Suggest to call stop() before join
/// Thread join could throw exception
/// https://en.cppreference.com/w/cpp/thread/thread/join
/// </summary>
void join();
protected:
/// <summary>
/// Thread job of processing input data
/// Note: Use check function is_stoping(), when true than interupt processing
/// </summary>
/// <param name="input">input data to process</param>
virtual void process(std::unique_ptr<TIn> input) = 0;
};
//////
// Implementation
//////
template<typename TIn>
StopableJob<TIn>::~StopableJob()
{
stop();
try {
// thread join could throw exception
// https://en.cppreference.com/w/cpp/thread/thread/join
join();
} catch (const std::system_error & /* err*/) {}
}
template<typename TIn>
void StopableJob<TIn>::run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_mutex);
if (m_running) {
// when runnig
m_stop = true;
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running = true;
m_stop = false;
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
process(std::move(input));
std::lock_guard lg(m_mutex);
m_stop = false;
// this is not while (end)condition because of lock guard
if (m_input_next == nullptr) {
m_running = false;
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
template<typename TIn>
bool StopableJob<TIn>::is_running()
{
std::lock_guard lg(m_mutex);
return m_running;
}
template<typename TIn>
bool StopableJob<TIn>::is_stoping()
{
std::lock_guard lg(m_mutex);
return m_stop;
}
template<typename TIn>
void StopableJob<TIn>::stop()
{
std::lock_guard lg(m_mutex);
if (!m_running) return;
m_input_next = nullptr;
m_stop = true;
}
template<typename TIn>
void StopableJob<TIn>::join()
{
if (m_thread.joinable()) m_thread.join();
assert(!m_running);
}
} // namespace Slic3r::GUI
#endif // slic3r_StopableJob_hpp_