Paste P231

(An Untitled Masterwork)
ActivePublic

Authored by dkazakov on Jun 4 2018, 4:39 PM.
= Krita Sprint: long fight with jaggy lines on OSX =
Two weeks ago we had a very nice and motivating sprint in Deventer, where all the members of Krita team geathered in one place and met each other. Boud has already written a good post [https://krita.org/en/item/krita-2018-sprint-report/] about it, so I will try to avoid repetitions and tell a saga of my main goal for this sprint... fixing OSX tablet problems!
=== Tablet event compression ===
Since the very first release of Krita on OSX we had a weird problem. When the user painted too quickly, the strokes became jagged, or, how we call it, "bended" (you can see an example of such line in the title image ofthe post). This problem happened because tablet events coming from the stylus were being lost somewhere on their way from the driver to Krita.
I should say that this problem has already happened multiple times on Linux and Windows. In most of the cases it was caused by some Qt updates that introduced/activated "input events compression": a special feature of Qt to drop extra tablet/mouse move events if application is becoming slow to process them in time. This feature is necessary for normal non-painting application, which do not expect a strong stream of tablet move events and can simply sink in it. The main symptom of such compression is that "jagged lines" almost disappear when you disable openGL canvas, and it was reported that on OSX this symptom is also present. I have already fixed such compression muiple times, both in Qt and in Krita, so I was heading to the spring in quite opimistic mood...
But I became less optimistic when I arrived to the sprint and checked Qt's sources: there was no events compression implemented for OSX! I was a bit shocked, but it was so. Tests proved that all events that arrived to Qt were successfully delivered to Krita. That was a bit unexpected. It looked like OSX itself dropped the events if the application's event loop didn't fetch them from the event loop in time (I still think it is so).
So we couldn't do anything with this compression: it happened somewhere inside operting system or driver. The only way out was to make main Krita GUI thread more responsive, but there was another thing... openGL!
=== Prevent openGL from blocking Krita's GUI thread ===
The main symptom of the compression problem, its disappearance when disabling openGL, was related to the fact that sometimes openGL needs quite a bit of time to upload updated textures of do the rendering of the canvas. Very simplified, our rendering pipeline looked like that:
1) Image is updated by brush
2) GUI thread uploads the textures to GPU using glTexImage2D or glTexSubImage2D
3) GUI thread calls QOpenGLWidget::update() to start new rendering cycle
4) Qt calls QOpenGLWidget::paintGL(), where we generate mipmaps for the updated textures and render them on screen.
This pipeline worked equally good on all platforms except on OSX. If we ran it on OSX, Krita would render the textures with *corrupted mipmaps*. Long time ago, when we first found this issue, we couldn't understand why it happens and just added a dirty hack that workarounded the problem: we added glFinish() betweed uploading the textures. It solved the problem of corrupted mipmaps, but it made the renderng loop slower. We never understood why it was needed, but it somehow fixed the problem, and the OSX-specific pipeline started to look like that:
1) Update the image
2) Upload textures
3) Call glFinish() /* VEEERY SLOW */
3) Call QOpenGLWidget::update()
4) Generate mipmaps and render the textures
We profiled Krita in apitrace [https://github.com/apitrace/apitrace] and it became obvious that this glFinish() is really a problem. It blocks the event loop maiking OSX drop input events. So we had to remove it, but why it was needed at all? OpenGL guarantees that all GPU calls are executed in a chronological order, why do they become reordered?
I spent almost two days at the spring trying to find out why this glFinish() was needed and two more days after returning back home. I even thought that it was a bug in OSX's implementation of openGL protocol... but the thing was much simpler.
It turned out that we used two separate openGL contexts: one (Qt's one) that we used for uploading the textures, and the other one (QOpenGLWidget's one) for rendering itself. These contexts were shared, so we though that they are equivalent, but they were not :( Yes, they share all the resources, but the way how they process GPU command queues is implementation dependent. On Linux and Windows they seem to share the commands queue as well, but on OSX not. The queues on OSX were separate, so the commands became reordered and we got corrupted mipmaps...
In real life our pipeline looked like that:
openGL conext 1:
1) Update the image
2) Upload textures
openGL context 2:
3) Call QOpenGLWidget::update()
4) Generate mipmaps and render the textures /* renders corrupted mipmaps, because uploading is not yet finished */
So we had to just move the uploading into the correct openGL context and the bug went away [https://phabricator.kde.org/R37:fb43d4e5be6112c7d9df2ee3f33697d07a614ca6]
The moral of the story:
Always take care about what openGL context you use for accessing GPU. If you are not inside QOpenGLWidget::paintGL(), the context might be quite random.
PS:
Of course, this patch hasn't fixed the tablet problem completely, because the compression still happens somewhere deep inside OSX, but it became almost impossible to notice it now :)
dkazakov created this paste.Jun 4 2018, 4:39 PM
dkazakov created this object in space S1 KDE Community.