From f9bee8757031e15296f459b36fe9f57c45c0ef80 Mon Sep 17 00:00:00 2001 From: Jabiertxof Date: Wed, 3 Feb 2021 20:13:54 +0100 Subject: [PATCH 1/2] Rendering in other thread This tiny MR is just to check if rendering on other thread than main improve speed in Mac as user Brenden Soares tell in issue 1614 https://gitlab.com/inkscape/inkscape/-/issues/1614#note_415422794 --- src/ui/widget/canvas.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index a9acea927e..1e5d7c4e2e 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -10,7 +10,7 @@ * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ - +#include #include #include @@ -964,10 +964,10 @@ Canvas::on_idle() if (!_drawing) { return false; // Disconnect } - - bool done = do_update(); + bool done = false; + std::thread t([&] {done = Inkscape::UI::Widget::Canvas::do_update();}); + t.join(); int n_rects = _clean_region->get_num_rectangles(); - // If we've drawn everything then we should have just one clean rectangle, covering the entire canvas. if (n_rects == 0) { std::cerr << "Canvas::on_idle: clean region is empty!" << std::endl; -- GitLab From e289981917a329f756aeef3d12fff2d07b481eeb Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Fri, 5 Feb 2021 00:37:52 +0100 Subject: [PATCH 2/2] Test threads with mesages --- src/ui/widget/canvas.cpp | 107 ++++++++++++++++++++++++++++++++++++--- src/ui/widget/canvas.h | 4 +- 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index 1e5d7c4e2e..a983bae273 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -10,7 +10,7 @@ * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include + #include #include @@ -30,7 +30,10 @@ #include "display/control/canvas-item-group.h" #include "ui/tools/tool-base.h" // Default cursor - +#include "ui/tools/select-tool.h" +#include +#include +#include /* * The canvas is responsible for rendering the SVG drawing with various "control" * items below and on top of the drawing. Rendering is triggered by a call to one of: @@ -89,6 +92,76 @@ struct PaintRectSetup { Geom::Point mouse_loc; }; +using clk = std::chrono::high_resolution_clock; +using time_point = std::chrono::time_point; +using dur_double = std::chrono::duration; +using std::chrono::duration_cast; + +class Timer { +public: + Timer(const std::string &cmd) : _cmd{cmd}, _start{clk::now()} {}; + + double time_ns() { + auto duration = clk::now() - _start; + auto elapsed_s = duration_cast(duration).count(); + return elapsed_s * 1000 /* * 1000 * 1000 */; + } + + ~Timer(){}; + +private: + std::string _cmd; + time_point _start; +}; + +// use other tool than select to non threaded measure +// https://lemire.me/blog/2020/06/10/reusing-a-thread-in-c-for-better-performance/ +// https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2020/06/10 +struct yield_worker { + yield_worker() = default; + inline ~yield_worker() { + stop_thread(); + } + inline void stop_thread() { + exiting.store(true); + has_work.store(false); + if (thread.joinable()) { + thread.join(); + } + } + + inline void work() { + has_work.store(true); + } + + inline void finish() { + while (has_work.load()) { + } + } + Inkscape::UI::Widget::Canvas *canvas = nullptr; + std::atomic done{false}; +private: + std::atomic has_work{false}; + std::atomic exiting{false}; + std::atomic thread_started{false}; + std::thread thread = std::thread([this] { + thread_started.store(true); + while (true) { + while (!has_work.load()) { + if (exiting.load()) { + return; + } + std::this_thread::yield(); + } + if (canvas) { + done.store(canvas->paint()); + } + has_work.store(false); + } + }); +}; + +yield_worker yw; namespace Inkscape { namespace UI { @@ -135,7 +208,7 @@ Canvas::~Canvas() _in_destruction = true; remove_idle(); - + yw.stop_thread(); // Remove entire CanvasItem tree. delete _canvas_item_root; } @@ -935,6 +1008,8 @@ Canvas::add_idle() } if (get_realized() && !_idle_connection.connected()) { + std::thread::id this_id = std::this_thread::get_id(); + std::cout << this_id << "::Main thread (Start iddle loop)::\n"; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint redrawPriority = prefs->getIntLimited("/options/redrawpriority/value", G_PRIORITY_HIGH_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT_IDLE); if (_in_full_redraw) { @@ -964,9 +1039,13 @@ Canvas::on_idle() if (!_drawing) { return false; // Disconnect } - bool done = false; - std::thread t([&] {done = Inkscape::UI::Widget::Canvas::do_update();}); - t.join(); + auto t = Timer{__FUNCTION__}; + bool done = do_update(); + if (dynamic_cast(SP_ACTIVE_DESKTOP->event_context)) { + std::cout << "duration: " << t.time_ns() << std::endl; + } else { + std::cout << "duration: " << t.time_ns() << std::endl; + } int n_rects = _clean_region->get_num_rectangles(); // If we've drawn everything then we should have just one clean rectangle, covering the entire canvas. if (n_rects == 0) { @@ -1001,7 +1080,14 @@ Canvas::do_update() _canvas_item_root->update(_affine); _need_update = false; } - return paint(); + if (dynamic_cast(SP_ACTIVE_DESKTOP->event_context)) { + yw.canvas = this; + yw.work(); // issue the work + yw.finish(); + return yw.done.load(); + } else { + return paint(); + } } // TODO: This makes no sense as normally we wouldn't reach here. @@ -1024,7 +1110,12 @@ Canvas::paint() if (_need_update) { std::cerr << "Canvas::Paint: called while needing update!" << std::endl; } - + std::thread::id this_id = std::this_thread::get_id(); + if (dynamic_cast(SP_ACTIVE_DESKTOP->event_context)) { + std::cout << this_id << "::Render thread (Select tool)::\n"; + } else { + std::cout << this_id << "::Main thread (No select tool)::\n"; + } Cairo::RectangleInt crect = { _x0, _y0, _allocation.get_width(), _allocation.get_height() }; auto draw_region = Cairo::Region::create(crect); draw_region->subtract(_clean_region); diff --git a/src/ui/widget/canvas.h b/src/ui/widget/canvas.h index a902088909..d3db0bc615 100644 --- a/src/ui/widget/canvas.h +++ b/src/ui/widget/canvas.h @@ -69,7 +69,7 @@ public: void redraw_now(); // Draw areas needing update immediately. void request_update(); // Draw after updating canvas items. void scroll_to(Geom::Point const &c, bool clear); - + bool paint(); void set_background_color(guint32 rgba); void set_background_checkerboard(guint32 rgba = 0xC4C4C4FF); @@ -157,7 +157,7 @@ private: // In order they are called in painting. bool do_update(); - bool paint(); + bool paint_rect(Cairo::RectangleInt& rect); bool paint_rect_internal(PaintRectSetup const *setup, Geom::IntRect const &this_rect); void paint_single_buffer(Geom::IntRect const &paint_rect, Geom::IntRect const &canvas_rect, -- GitLab