phillipberndt / pqiv

Powerful image viewer with minimal UI
http://www.pberndt.com/Programme/Linux/pqiv/index.html
GNU General Public License v3.0
308 stars 44 forks source link

Unwanted tearing effect in window mode #92

Closed pa-d-v closed 7 years ago

pa-d-v commented 7 years ago

pqiv version 2.8.5 In window mode with slideshow enabled when swithing from portrait to landscape images it looks like first the window is made wider half on the left and half on the right still showing the portrait image shifted to the left before showing the landscape image which gives an unwanted tearing effect.

phillipberndt commented 7 years ago

I just pushed the branch redraw, which outputs debug messages whenever the window is redrawn or the size changes. On Cinnamon, I only see one configure and one draw event when I switch between two files (of different size), so I currently suspect that this is an issue in i3. (Edit: I just tested this, I only get one draw event in i3 [with pqiv floating], too..)

If I remember correctly, this behavior is the result of a compromise for making pqiv work with some non-compliant WMs, but I'll investigate further once I find the time.

pa-d-v commented 7 years ago

When I set --window-position=1,1 everything looks good since it tries to expand the window only to the right. But with no window-position specified I have it since it tries to expand the window in both directions. (--window-position=0,0 doesn't give the expected result and centers the first image as is the case with off, would expected it to be completely top left in the screen)

phillipberndt commented 7 years ago

But with no window-position specified I have it since it tries to expand the window in both directions.

It's up to the WM to decide how to realize a resize request, AFAIK, I can't influence that, sorry. (If you find a way let me know, I'll include a fix in pqiv!)

BTW, a simple solution is to make the window tiled instead of floating, pqiv is designed to work with tiling WMs.

--window-position=0,0 doesn't give the expected result and centers the first image as is the case with off, would expected it to be completely top left in the screen

That's a bug in i3 (or, likely, intended behavior). For me, it ignores requests for positions with x<10 or y<10 altogether. pqiv does issue a position request to 0,0 with this parameter, and other WMs honor this request.

pa-d-v commented 7 years ago

You're right --window-position=0,0 is honoured in mutter, when no --window-position is specified it looks like pqiv centers the image both in mutter and i3. With images that fit the screen this tearing effect does not happen, so likely it has something to do with scaling.

phillipberndt commented 7 years ago

Are you using GTK 2 by any chance? Have you tried compiling with GTK 3 instead? The internal drawing model changed between the two versions. If this works, this'll still be only a workaround, but that's still better than nothing I guess. I can reproduce the issue with GTK 2, but have no clue how to resolve it right now :/

phillipberndt commented 7 years ago

I might have a solution after all. See the gtk2-notearing branch: It adds some overhead (everything is drawn twice if the window size changes), but it should resolve the major visual problems.

Edit: Too bad.. this is another case of different WMs behaving differently. While the current version looks much better in Mutter, the fullscreen/unfullscreen transition looks worse in i3, because it's too fast :-) Visuals of windows resizing have improved though. I'll think further about this.

pa-d-v commented 7 years ago

pqiv was compiled with GTK 3. When I compile the branch with GTK 2 the tearing is gone (thanks!) but with GTK 3 it is still there in i3 wm.

phillipberndt commented 7 years ago

I see. With Mutter, only GTK 2 is affected; I assumed it'd be the same with i3. I've been playing around with this a bit.. my impression right now is that the true reason for the issues is that rendering the images takes too long. (The current version in the branch resolves almost all of the problems with its latest commit - which basically caches the scaled image for the new window size before the actual resizing takes place.)

For now, the version from the branch is a good workaround, but I won't merge this to master as it is. Instead, I'll rework the scaling such that images are already scaled to their prospective display size before they are displayed and check whether that solves the issue.

phillipberndt commented 7 years ago

The changes might be easier to implement than I initially thought. Please have a look at the prerender-size branch. It resolves the issues for both gtk3 and gtk2 for me (and improves performance in general).

pa-d-v commented 7 years ago

It is much faster/performant than before but I still see tearing (when going forward from portrait to landscape image the previous portrait image is breefly shown shifted to the left) with i3 in window mode while the original qiv doesn't have this with i3 in window mode on forward move but on backward move which is less used.

pa-d-v commented 7 years ago

The notearing branch with GTK 2 doesn't have this forward tearing issue and behaves similar to the original qiv.

phillipberndt commented 7 years ago

Too bad :/

when going forward from portrait to landscape image the previous portrait image is breefly shown shifted to the left

What you see is the same content as the portrait image had, top-left-aligned in the now wider window.

The notearing branch with GTK 2 doesn't have this forward tearing issue and behaves similar to the original qiv.

That's because the other version uses an alternative render path. There, I manually set the background pixmap to what will be visible after the actual drawing pass before resizing the image such that X11 (or your compositor) already knows what to draw into the window. GTK 3 removed this API (for good reasons), so I cannot port this solution to GTK 3, and hence it's not a good solution to go forward with.

I could draw the new image before resizing the window, but then you'd still see the parts of the window which weren't visible before flicker (black) and we'd have a (otherwise unnecessary) second drawing pass. Blanking the window entirely before resizing improves the looks in i3 as well, but introduces flickering in the first place for WMs which delay displaying the resized window until they have obtained the visuals to display in it.

I think that I'll have to ask the GTK devs for ideas.

phillipberndt commented 7 years ago

I've just had another look and found that I've had a off-by-one-pixel error after all, leading to cache misses upon image transitions. Did the latest commit in the prerender-size branch improve the situation for you?

pa-d-v commented 7 years ago

No with latest prerender-size branch still tearing in i3 wm with GTK 2 and 3.

phillipberndt commented 7 years ago

:(

I've looked a bit deeper into this.. qiv uses the same trick I used in the other branch to fix the issue. Problem is: It is not portable to GTK 3 and it works exclusively with X11. So this isn't an option.

All I've read so far seems to indicate that with GTK 3, there is no portable way to draw outside of the current bounds of a window. Since if you resize a X11 window X11 will preserve the window's contents and fill any new areas with a given background, there will always be a flicker between the resizing and the update of the window.

All I can really do is draw the new contents of the window into the visible part of the window before resizing such that the flicker is at least restricted to new parts of the window being blackened out. That's what the current (more recent than the one you've tested) version in the branch does. (Even if this improves things I'd have to make this a configurable feature though, because this has negative impact on other WMs.)

Edit: Some more progress. I do now at least know how to detect this situation. There's a Frame Synchronization WM spec addressing this issue which i3 doesn't support.

pa-d-v commented 7 years ago

Thanks for investigating. With the latest prerender-size branch the tearing is gone but we have the flickering for the new parts. How would this feature be activated: with a runtime command line option or is it possible to automatically detect when the wm doesn't support frame synchro and use it then (that would be great)? To have best of both worlds it would also be nice if compiled with --gtk-version=2 that the flickering would be eliminated by using gtk2-notearing either by autodetecting if we use X11 or an extra compile time option if that's easier.

phillipberndt commented 7 years ago

It's possible to detect whether a WM supports this, GTK uses the detection internally, that's how I learned about this in the first place. So yes, this feature would activate automatically.

I've just had an idea on how this fix could be brought to GTK 3 as well. I'm using Xlib for another bugfix anyway, so it should be possible to manually set the background pixmap. I'll test whether this works and get back to you.

phillipberndt commented 7 years ago

I've just committed a new version to the branch. I now manually assign a background pixmap to the window, which works for GTK 3 as well. It doesn't look perfect for transitions from large to small images yet, but if I'd fix that, non-floating pqiv windows would flash a tiled version of the image when transitioning between files.

pa-d-v commented 7 years ago

Looks very nice no more tearing and flickering both with GTK 3 and 2. When running pqiv -f right away it gives "segmentation fault (core dumped)". (Also when moving forward between images in zip archive I notice completely black image flickering probably because libarchive being to slow)

phillipberndt commented 7 years ago

When running pqiv -f right away it gives "segmentation fault (core dumped)".

Thanks, fixed

(Also when moving forward between images in zip archive I notice completely black image flickering probably because libarchive being to slow)

That's one of the side effects of the tiling mode support.. I've tried adding a way to detect tiling mode (I check whether the WM reports the window size I've requested) now, this should remove the flickering.

Could you please test once again whether transparency still works? I don't have a compositor for i3 installed.

pa-d-v commented 7 years ago

pqiv -f doesn't segfault anymore now but the first image is always completely black. --transparent-background in fullscreen mode works with compositor, I see my background at the sides. No black flickering anymore with image in zip archives, nice! With i3 wm in window-mode sometimes when moving fast between images in zip archive with Space a landscape image is shown smaller in a portrait rectangle with black bars at the top an bottom (libarchive to slow?). When switching between window-mode and fullscreen mode with f key sometimes images are shifted a few pixel to the left giving a motion effect, I would expect images to remain in te same place when doing this. Also sometimes when switching from fullscreen to window-mode portrait images are completely shifted to the left on the place where a landscape image starts and landscape images are completely shifted to the right on the place where a portrait image starts.

pa-d-v commented 7 years ago

Also with i3 in tiling mode e.g. half the screen I would expect an image to be scaled within the tiled window now the fullscreen image is trimmed at the sides and pressing f twice gives the wanted effect. Otherwise I can file a seperate bug for this.

phillipberndt commented 7 years ago

Nice catch. That was a new bug introduced by one of the recent changes. Should be fixed now.

pa-d-v commented 7 years ago

Thanks for fixing tiling mode but when switching to fullscreen I breefly see the second image repeated several times next to each other. Perhaps related to pqiv -f where the first image is completely black, did you notice my first comments?

phillipberndt commented 7 years ago

Thanks for fixing tiling mode but when switching to fullscreen I breefly see the second image repeated several times next to each other.

What you see is the background set to avoid flickering of the window; X11 automatically tiles it. The only alternative is to blank the window, I've changed the code to to that. Let me know if you preferred the old behaviour. (As before, this doesn't affect WMs with Framesync support.)

Perhaps related to pqiv -f where the first image is completely black, did you notice my first comments?

No, that was a slightly weird timing issue in the gtk<->wm communication. Should be fixed.

pa-d-v commented 7 years ago

Looks good now. Only thing I still notice sometimes when switching from fullscreen to window-mode portrait images are completely shifted to the left on the place where a landscape image starts or landscape images are completely shifted to the right on the place where a portrait image starts. Can something be done about it?

phillipberndt commented 7 years ago

Switching from fullscreen to both floating and tiled windows works fine each time for me. I'm testing with i3 4.11, on a 1280x1024 screen, with an X11 connection tunnelled through several remote servers back to my desktop tu ensure that I have enough latency (~72 msec according to x11perf) to see any issues that might arise. As soon as I manage to reproduce this issue, it's probably solvable - but I'll need some more info to do that :-)

pa-d-v commented 7 years ago

I think I found a way to reproduce with set of portrait and landscape images even without slideshow mode it happens after swithing fullscreen on and off 2 times like this: press f, zero or more times space, f, zero or more times space, f, space until scape changes and finally f again. e.g. first 3 portrait images and then 1 landscape image (set Y9): press f, f, f, 3 times space until landscape and f now landscape image is shifted to the right at the position of portrait image. Pressing f, space, f, space, f, space and f would have given the same result here. Or e.g. first 7 images landscape and then 1 portrait (set Ye): press f, f, f, press 7 times space until portrait image and f now portrait image is shifted to the left at the position of landscape image. Pressing f, 2 times space, f, 2 times space, f, 3 times space and f would have given the same result. Hope you can reproduce too otherwise I can provide examples sets Y9 and Ye.

phillipberndt commented 7 years ago

Do you mean the flickering one sees when doing the final switch back into window mode? Or does the view actually remain in a distorted or otherwise wrong way? I can reproduce the former, but not the latter.

pa-d-v commented 7 years ago

No I don't mind the flickering. I can reproduce both with debian jessie i3 4.8 and manjaro-i3 4.13, screencast in attachment of set Y9: 3 portrait images and then 1 landscape image. I ran pqiv 1.png 2.png 3.png 4.png in floating window mode and pressed f, f, f, 3 times space until landscape image 4 appears and then final f. Now image 4 appears shifted: the topleft corner is the same as where portrait images start while I would expected it to be centered (like in fullscreen mode). y9

phillipberndt commented 7 years ago

Ah, I see. The window position is wrong. I'll see what I can do about that!

phillipberndt commented 7 years ago

I think I resolved the issue by postponing moving/resizing the window when leaving fullscreen until a configure event arrives. Did the latest change fix the behaviour?

Edit: Please test with the latest master branch, I have merged the changes in preparation of the next release.

pa-d-v commented 7 years ago

Tested with master this seems to be a lot better but I still get wrong window position sometimes when pressing f and space at the same time (tested with 3 landscape images 4000x2667 and then 1 portrait image 2667x4000): 2017-06-18-181258_1920x1080_scrot Wrong window position with image 1 and 3 and landscape image 1 is shown in portrait container with black bars. 2017-06-18-181338_1920x1080_scrot Also sometimes when pressing f and space at the same time in fullscreen mode the image is repeated: 2017-06-18-181717_1920x1080_scrot

phillipberndt commented 7 years ago

That's an i3/gtk communication issue. pqiv requests fullscreen, but does not internally switch to fullscreen drawing until it receives a state event. While in this transition phase, if you switch images, pqiv will still issue a request to resize/move the application window to the new position. This causes confusion about the state in either i3 or gtk. Probably gtk, because Mutter is affected as well.

I can fix this by simply assuming that any fullscreen requests will be honored, but that'd lead to problems with WMs that forbid fullscreen transition intentionally. Since this has valid use cases, too, I'd rather not break it. As a compromise, I now wait for half a second for a state event after attempting to change state, and do not attempt to resize the window during that time frame.

pa-d-v commented 7 years ago

Looks like a difficult issue: still able to reproduce 1 with black bars and 3 when pressing f and space at the same time every second and sometimes even completely black screen instead of image both with gtk 3 and 2. Sometimes part of the image is shown and the rest filled with checkerboard pattern.

phillipberndt commented 7 years ago

I know; that's part of the compromise.. luckily, pressing both keys simultaneously isn't something you'd normally do, and this doesn't happen unless you do, right? (The only thing I could do about that is blocking user input while waiting for the fullscreen transition or for an image to load, such that both necessarily happen sequentially rather than in parallel, and I think that that'd do more harm than good. Especially since most WMs don't have this issue.)

phillipberndt commented 7 years ago

I've had one other idea that might improve things with respect to the black bard situation, that's now in master.

pa-d-v commented 7 years ago

Also landscape image 1 of 4000x2667 is shifted one pixel to the left and top when switching from window mode (151 px from the left on my 1920x1080 screen and 149 px from the right and 1 px from top) to fullscreen mode (150 px from the left on my 1920x1080 screen and 150 px from the right and 0 px from top), can these match?

phillipberndt commented 7 years ago

Sadly no. As far as pqiv is concerned, the window is perfectly centered, and the pictures in fullscreen are perfectly centered. (I could of course add a workaround, but the actual shift is depends the WM and probably on the theme. Cinnamon does for example move windows a bit up to center them within the space available above the task bar, so that images shift vertically.)

pa-d-v commented 7 years ago

You are right pressing f and space simultaneously won't happen (I can still reproduce wrong window position like image 3 with images in zip archive in window mode by just pressing space quickly, libarchive being to slow?). The black borders seem to be gone now, thanks! The original qiv does not have this 1 pixel shift between window mode and fullscreen mode with same i3 wm config and same theme.

phillipberndt commented 7 years ago

The original qiv does not have this 1 pixel shift between window mode and fullscreen mode with same i3 wm config and same theme.

With an image that is 500px wide on a 1280px wide screen, the image should be placed 390px from the left. On my PC, that's exactly where the window's border starts with i3, and that's why images are shifted. That's to say i3 regards the outer top left point, where the window decoration starts, as the window's position, not the first pixel that the application can actually draw onto. If I use -c (which does not only make the background transparent but also disables window decorations) then the image positions match perfectly in a default i3 setup.

That being said, you can fix this yourself by rebinding the f key to also shift the image: (..this was actually broken by the changes I've made while fixing this issue, but works again)

[keybindings]
f { send_keys(#fs1); }
<numbersign>fs1 { toggle_fullscreen(1); shift_x(1); bind_key(f { send_keys(#fs2\); }); } 
<numbersign>fs2 { toggle_fullscreen(2); bind_key(f { send_keys(#fs1\); })); }

This way, every other press of f switches to fullscreen and simultaneously shifts by 1px.

pa-d-v commented 7 years ago

With pqiv -c and clicking on the window border to remove I still see 1 pixel shift on portrait image 2667x4000 while landscape images 4000x2667 are not shifted comparing window mode and full screen. I'll test further with the keybindings...

pa-d-v commented 7 years ago

Can something be done about wrong window position in zip archives when just pressing space and no f at all?

phillipberndt commented 7 years ago

Can something be done about wrong window position in zip archives when just pressing space and no f at all?

The problem is this: GTK tries to be intelligent when issuing requests to resize or move the window. It tries to avoid unnecessary requests to save bandwidth, and it does not interfere if it receives a reply indicating that the request wasn't honoured because WMs aren't obliged to. (As is the case for tiling WMs, obviously.) If you switch betwen images quickly, the following can happen: You press space for the first time. pqiv requests the window to be resized to portrait size and moved into a centered position. While it is still waiting for a reply to this request, you hit space again and pqiv is so quick that it actually loads the next image still before receiving an acknowledgement that resizing and repositioning the window worked. As soon as the image is loaded, it then requests that the window is resized and moved into place at the center of the screen. GTK does its magic, and sees that the window actually already has the correct size and position (since it didn't receive an answer from i3 yet). Since pqiv explicitly requested a resize, GTK doesn't default to doing nothing, but instead sends a resize request with the known size to i3 - not accompanied by a move request, because from its point of view, the window still is in the correct position, and thus the move request can be saved.

The only way around this is to make pqiv wait for replies before progressing. I'll see if I can do that without doing any harm.

phillipberndt commented 7 years ago

The only way around this is to make pqiv wait for replies before progressing. I'll see if I can do that without doing any harm.

As expected, I can't: While this fixes the behaviour, it also results in a complete drawing pass for the intermediate window size, which slows down transitioning between images considerably; especially for WMs with frame sync support. Also, pqiv now behaves mostly as if --recreate-window had been used in terms of responsiveness - would that option perhaps be an alternative?

phillipberndt commented 7 years ago

I've had another idea for a workaround, and this looks as if it'd work in preliminary tests. Could you please check if the latest commit fixes the issue?

pa-d-v commented 7 years ago

Wrong window position in zip files is gone now, thanks!

With pqiv -c and --scale-mode-screen-fraction=1 clicking on the window border to remove I still see 1 pixel shift on portrait image 2667x4000 while landscape images 4000x2667 are not shifted comparing window mode and fullscreen. In fullscreen mode I get with portrait image 2667x4000 599 px from left and 601 px from right while in window mode this is 600 px both (on my 1920×1080 screen). Keybindings can't solve this since the shift isn't equal for same size images in portrait and landscape.

phillipberndt commented 7 years ago

Wrong window position in zip files is gone now, thanks!

Great.

I still see 1 pixel shift on portrait image 2667x4000 while landscape images 4000x2667 are not shifted comparing window mode and fullscreen.

It is very interesting that this depends on the window's aspect ratio. I can reproduce this in i3, but as I've said, pqiv does request that the window is positioned exactly centered and does draw the image exactly centered in fullscreen mode. It is i3 that decides to offset the window from the requested coordinates. (And it indeed does - my assumption that this was related to the window border is wrong, i3 actually moves the window to a position offset from GTK's requested position according to the configure events it receives after issuing the move/resize request. Interestingly, this only affects me the first time the window is moved/resized - subsequent requests are honoured. Even if I switch to fullscreen and back, the window is then centered correctly, even though the requested position is exactly the same.)

(Edit: This appears to be a very common issue btw, Mutter does as well regard a requested position as the outer window position, such that X11 sends a configure event offset by the size of the window border. There's probably even a clever reason for this behaviour..)

phillipberndt commented 7 years ago

I have added a workaround that works for me: A new action move_window(x, y), which can be used to explicitly recenter the window if invoked as move_window(-1, -1). Since only the startup position is an issue for me, it suffices to add

[actions]
move_window(-1, -1);

to my pqivrc. This will recenter the window after startup, moving it to the correct position.

This will of course only help if you suffer from the same problem, that is, that i3 offsets the first requested position.

pa-d-v commented 7 years ago

Remains the same with 2.9 and above pqivrc, do you need me to provide screenshots?