Bug 476300

Summary: Brush outline not fully rendered on cursor movement
Product: [Applications] krita Reporter: Lynx3d <lynx.mw+kde>
Component: OpenGL CanvasAssignee: Krita Bugs <krita-bugs-null>
Status: REOPENED ---    
Severity: normal CC: dimula73, tomtomtomreportingin
Priority: NOR Keywords: regression
Version: 5.2.0   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: Canvas Screenshot taken while moving cursor

Description Lynx3d 2023-10-29 19:14:36 UTC
Created attachment 162703 [details]
Canvas Screenshot taken while moving cursor

SUMMARY

When the cursor/stylus moves a bit faster, the brush outline is not fully rendered (see attached screenshot).
From my attempts to debug it, it seems the dirty area of the canvas widget lags behind the actual tool outline that gets rendered by one frame.
With small curors, the outline basically becomes invisible when moving the cursor fast.
As soon as the movement stops, it always seems to render fully though.

It is definitely an issue with the partial canvas update, because when I force full canvas redraw in code (as is done when fractional UI scaling is enabled), or use an unpatched Qt, it goes away.

This did not happen with 5.1.5, but happens with 5.2.0 and current master (tested appimages and my own builds).

ADDITIONAL INFORMATION

Ubuntu 22.04, nVidia GTX 960, KDE Plasma 5.24.7 (X11)
Comment 1 tomtomtomreportingin 2023-10-29 22:08:48 UTC
Can confirm in Debian sid, AMD RX 580, KDE Plasma 5.27.9 (X11).
Comment 2 Dmitry Kazakov 2023-10-30 08:31:05 UTC
Okay, the problem happens due to a really complicated reason. The point is that the canvas updates are compressed twice: first by Krita itself (in KisCanvas2), and then by Qt (in QPaintEvent). And the problem happens when the brush outline gets updated while Qt-level compression happens. Which means that the update comes with the old clip rect, but the outline is already updated to the new one. 

I wonder how we could fix or workaround that...
Comment 3 Dmitry Kazakov 2023-10-30 09:20:25 UTC
Git commit 6fe21bf863bef481076c1bb560132be28c7c0990 by Dmitry Kazakov.
Committed on 30/10/2023 at 10:20.
Pushed by dkazakov into branch 'krita/5.2'.

Add update-ahead feature to outline updates

Due to a compression for widget updates in Krita and Qt, the brush
outline may change while the updates are being passed through the
pipeline.

We can either pass the outline alongside the update itself (which is
a bit complicated) or just do a simple update-ahead. This patch
implements the latter approach. It just calculated an approximation
of the next outline position and extends the update rect to that.

M  +41   -1    libs/ui/tool/kis_tool_paint.cc

https://invent.kde.org/graphics/krita/-/commit/6fe21bf863bef481076c1bb560132be28c7c0990
Comment 4 Dmitry Kazakov 2023-10-30 09:20:42 UTC
Git commit 4bbb383c9c3944e208cb78b6347eacea296e32ce by Dmitry Kazakov.
Committed on 30/10/2023 at 10:20.
Pushed by dkazakov into branch 'master'.

Add update-ahead feature to outline updates

Due to a compression for widget updates in Krita and Qt, the brush
outline may change while the updates are being passed through the
pipeline.

We can either pass the outline alongside the update itself (which is
a bit complicated) or just do a simple update-ahead. This patch
implements the latter approach. It just calculated an approximation
of the next outline position and extends the update rect to that.

M  +41   -1    libs/ui/tool/kis_tool_paint.cc

https://invent.kde.org/graphics/krita/-/commit/4bbb383c9c3944e208cb78b6347eacea296e32ce
Comment 5 Dmitry Kazakov 2023-10-30 09:24:00 UTC
Hi, Lynx3d and Tomtomtom!

Could you please test an updated version of Krita with my fix? 

https://binary-factory.kde.org/job/Krita_Stable_Appimage_Build/2035/

My fix will not fully fix the tearing. I can still see some tearing when moving the cursor in random directions, but it still makes outline look much better in less quick movements.

Is this fix enough for the problem? Or I should investigate further into a more complicated approach?
Comment 6 Lynx3d 2023-11-01 05:26:17 UTC
I'm afraid It does not work very well for me, small brushes like a pencil for sketching are still pretty much disappearing completely when moving a bit faster. That's really really not very nice when working with a screenless tablet.
(Btw. "Rulers Track Pointer" still hurts cursor smoothness for me and makes the movement guessing even less effective).

From what I understand, only pushing the update rect through Qt once when the framerate limiter kicks off a new frame is not enough when we use QWidget::update(...) rather than the immediate repaint(...), as Qt will process further events until it decides it's time to refresh.

Apparently you can fix the issue by calling QWidget::update(dirty_rect) again each time the tool outline has changed,
adding a "m_d->canvasWidget->updateCanvasDecorations(m_d->savedOverlayUpdateRect);" to  KisCanvas2::updateCanvasToolOutlineWdg() seems to work.
Though Qt has to assume the previously passed areas are still relevant when in fact the cursor will never be painted at those previous positions. And you'd need to to do extra state tracking to not break the frame rate limiter.
Thinking about it, why do we even compute brush outlines that never get rendered...

To be honest, I think we should just drop the Qt patch for tracking partial updates and simply keep the the dirty area in the canvas widget, seems simpler and less overhead...unless someone really manages to optimize compositing in the Qt backend.