UI Repaint Issue on Chrome
A while back, a section of our users started reporting a UI rendering issue while using Postman. The interesting thing about the issue was that it occurred only on very high resolution monitors. High resolution to the tune of 2k, 3k displays and above (such as Macbook Retina displays.) As such, full HD users did not face this issue. Having said that, it was quite annoying for the ones facing it.
Intermittently, parts of the UI would not redraw itself for no reason whatsoever and usually resizing the app window would fix it momentarily. There was no predictable way to reproduce the issue and nor did we discover the root cause of the issue. The screenshots submitted by our users were the only starting point we had.
Google Chrome team were tracking a number of similar issues but none provided any insight relevant to our case:
- https://code.google.com/p/chromium/issues/detail?id=412414
- https://code.google.com/p/chromium/issues/detail?id=383261
- https://code.google.com/p/macvim/issues/detail?id=429
- https://code.google.com/p/chromium/issues/detail?id=324691
Fixing this turned out to be a wild goose chase for a while, followed by endless experiments and confusion. We even posted a question on StackOverflow and Chromium issue tracker (all pending answer.) Eventually, we got on top of it and managed to fix it.
How did we approach solving this?
When any unexpected behaviour is discovered in our product, our cursory approach is to determine the exact steps to reproduce the bug and accurately map it onto the root cause of failure. This, however, was not applicable in this case since we had neither – the issue would unpredictably appear after interacting with the App for some time.
In order to eliminate the possibilities, we asked ourselves, the most definite things, we were aware of regarding this issue.
- It is independent of the operating system it is running on.
- There is negligible layout difference in higher resolutions that can account for this anomaly.
Based on these, our best guess was this issue was related to inaccurate calculation of redraw regions within browser. We also suspected that it could be a side-effect of GPU accelerated rendering.
Taming the GPU
To eliminate the possibility of GPU accelerated composting, we disabled the applicable Chrome flags (chrome://flags/
.) Unsurprisingly, that had no effect on getting towards a fix. This led us to ponder that the issue at hand appeared similar to the Windows form repaint issues we used to see.
As a result, we added a whole bunch of CSS hacks we knew that would improve rendering performance in high resolution displays.
@media (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) { body { transform: translateZ(0); backface-visibility: hidden; perspective: 1000; } }
And of course, it did not help our cause. (:meh:)
Borrowing from instincts
Assuming the DOM layout calculation as culprit, we started investigating the reflow characteristics of our app. Interestingly, the repaint failure was confined to the bounds of the main container alone – the sidebar and top navigation remained unaffected.
Thus, we added overflow: auto;
on main container to force extra calculations be performed while positioning the element. This is a direct inference from the bygone float
and overflow
related zero-height bugs that had, in turn, conceived the infamous clearfix solution.
This subdued the issue to some extent (or that is what we thought.) Within a couple of hours after releasing a beta fix, it was reported that the fix has made little or no difference. (Thanks for getting back to us so quickly)
Left with no other choice, we even tried combining the previous two approaches by adding forced GPU acceleration on the erring container element. Here, we must mention that this type of fix had previously helped others – just Google “chrome redraw issue”. We, on the other hand, were not so lucky.
Back to grind mode.
***
The greatest discovery of Chromekind
Knowing that the overflow: auto;
had reduced the issue (at least on a few systems in our lab,) we probed for CSS attributes that were manipulating the default overflow characteristics of elements. We found that we were beautifying our scrollbars to provide better user experience and that had overflow property specified. Based on the fact that there are already known issues in styling the Chrome scrollbars, we went on to turn off scrollbar styling and fall back to the default Chrome scrollbars. The issue appeared to have been conquered.
Having done that, we now needed to restore the user experience around scrollbars and started to design styling for a scrollbar with minimal change but usable still. All these while keeping fingers crossed that the minimal styling would not re-introduce the bug.
Just then, Abhinav exclaimed from the other end of our lab – “No!”
Alas! The issue was still there and it was occurring only when he plugged in the mouse! What?! (:sigh:) We really could no longer take any more flavours of this issue!
The fixer
After some time, we gathered ourselves and asked – “What is the difference between plugging in a mouse and not plugging in a mouse in OS X?” Guess what… permanently visible scrollbars!
We could now reproduce the issue more accurately and also control it by simply making OS X show or hide scrollbars on overflowing elements. The reason removal of our scrollbar styling had somewhat worked is because our CSS had made the scrollbars permanently visible while default behaviour is to show scrollbar only during scroll.
With these additional inputs and the mention on Github issue #705 by GitHub user @Quagh that just when the issue occurred, his system had switched from dedicated nVidia graphics processor to the integrated intel graphics card, we tried this:
We went through all elements that had overflow: scroll;
and added -webkit-transform: translate3d(0,0,0);
to force better hardware acceleration on those elements.
Hello world – we finally have the fix! It will be available automatically if you are using our App version 1.0.6+. Henceforth, we will be doubly diligent in making sure this does not happen. Heartfelt thanks to the chromium contributors and users for consolidating all pertinent issues that allowed us to better narrow down the cause and fix the issue.
If you liked how we were able to nail this issue and want to be a part of our team in Bangalore, feel free to drop us a note at [email protected].
Thank you for this post! Fixed our stuff with this!
That’s great.
Amazing research! Thats some engineering right there =)
I’ve found an issue with the solution though that makes it a non-starter for us. -webkit-transform: translate3d(0,0,0); messes with position somehow, which in particular blows up Bootstrap modals. Have you seen this before?
Bang on target! But, this is not as much as a bug as it is an intended behaviour. Performing 3D transformation on any element causes it to reset the viewport origin to the coordinate of itself. As such every element inside the 3D transformed element receives a coordinate system whose 0,0 becomes the left-top of the element being transformed.
In our case, this was not an issue since we did not have any absolute positioned item anywhere. The little we had (the search boxes), we moved them out of the scrolling elements on which we applied the fix.
Happy I found this post. Thanks for sharing your solution, I was using something similar to fix the same issue “-webkit-transform: translateZ(0);”.
Thanks for sharing, it’s useful
You guys are awesome. I thought I was insane when I saw this issue on my retina screen. I put the app on my other screen (thank goodness it wasn’t high res) and it was working! This post helped me solve the issue in a fraction of the time it would have otherwise taken. I put -webkit-transform: translate3d(0,0,0); on the elements with custom scrollbars, boom. Fixed. Instantly.
I think this issue is solved in latest version.
Btw thanks for sharing this awesome fix. I will try it in new version if it is not fixed yet.
U Saved the World!!!!