Krita/KisCanvas2 Update Split Reasoning

From KDE Community Wiki

KisCanvas2 Update Split Reasoning

As all of you know, atm there is flickering on the canvas when you paint. This flickering is caused by a race condition between the image (KisProjection) thread and ui (KisView2(?)) thread. It happens when the ui wants to read image data from the projection, but the projection is updating the same very place.

So, here are the details of my idea how to solve this issue:

The thing we need is to implement a two-stage update of the cavnvas. These stages will do the following:

  • The first stage will be executed in a context of the merger thread. KisProjection will deliver a syncronous signal to the canvas and the corresponding slot of the canvas will read the data from a projection. At that moment of time the area of the projection will be locked by a merger thread so the data can be read safely to a cache. After that the update slot will emit a signal for the KisView2 thread and return control to the merger object. The merger, in its turn, will unlock the area and continue its work.
  • The second stage (asynchronous part) will be executed on behalf of the ui thread and triggered by a signal emitted in the end of the previous stage. It will read the data from the cache, scale it down using KisPrescaleProjection or KisImagePyramid and update the widget object with a new piece of picture.

The problem here is that KisPrescaledProjection and KisImagePyramid objects must be stateless. Why? There are at least two reasons for this:

  • A part of the first stage should be done by these very backends and should use some preliminary calculations like zoom-level and alignment of the image pyramid, though the backends should not do actual scale-down in the context of the image thread (they have their own thread for this). At the same time, ui thread can request scale-down simultaneusly with the preliminary calculations. If KisPrescaledProjection was not stateless, it would surely lead to race conditions.
  • In a long-term plan the actual scale-down process could be done in several threads so making scaling process stateless is inevitable.

How will it work:

  1. KisCanvas2 gets a synchronous signal and asks KisPrescaledProjection to update a cache.
  2. KisPrescaledProjection updates a cache and fills-in some structure (let's call is KisUpdateInformation) with some internal stuff like scale factor, image rect in KisImage pixels, viewport rect in widget pixels and etc. It looks like a Memento design pattern.
  3. KisCanvas2 returns control to the merger thread and sends an asynchronous signal to the ui thread, passing the KisUpdateInformation struct as a parameter.
  4. Ui thread continues the update process using the state saved in KisUpdateInformation.