Mac multi-display bug (on non-retina Macs) with Pyglet on PsychoPy3 (but not 2)

Aside from my previous struggles with retina displays, I’ve discovered a novel bug in PsychoPy3 that is not present in 1.90.2 or 3 involving windows and multiple monitors. This is for a setup where we have an iMac which does not have a retina display, connected to a second monitor that’s a relatively old Apple cinema display. The iMac built-in display has a resolution of 1920x1080 and the secondary monitor is 1280x800.

Basically, the bug is very reliable, relatively minor, but very strange: If I flip the window on the external monitor but don’t draw anything to it, and also flip the window on the primary monitor with some objects in it on the same loop, it distorts the objects in the primary monitor, sizing them up at least 4x and moving them to the right. It snaps back as soon as I flip the window on the primary monitor again, or I draw an object to the window on the secondary monitor and flip both.

This is more of an annoyance than anything else, but it doesn’t happen on PsychoPy2 so I’m curious what could be causing it, and if there’s any way to fix it. I know that pyglet and MacOS don’t get along very well when it comes to having multiple displays, but given that it is otherwise working fine, this seems like it should be fixable. Anyone have any ideas why the specific conditions of flipping without drawing any objects would do this?

Update: Not just multi-display. Multi-window. It happens on one screen too.

Here are some screenshots of what’s happening. I’ve narrowed it down a little.

This is what it’s supposed to look like, and what it looks like when the movie (in the “main” window, in the background) is playing, drawing every frame and flipping every frame. WaitBlanking is false on the front window.

This is what happens if I flip the rear window when the movie is paused, whether or not I tell it to draw the movie object.

I’m not sure what’s causing this. It doesn’t occur with the ScreensAndWindows demo if I don’t draw one of the windows for a bit, so I’m wondering if it’s partly something about the movies specifically, but I have no idea how a movie stimulus in one window could have any effect on the other window…

Isolated it. This bug occurs when you have two different pyglet windows of two different sizes. Oddly, objects in each window seem to scale to the other window’s size. I’ve tested this a bunch of different ways using screensAndWindows.py. Let me lay out some things in fine detail.

Here’s how the windows are being set up:

if True:  # use two positions on one screen
    winL = visual.Window(size=[600, 450], pos=[100, 200], screen=0, 
                         allowGUI=False)
    winR = visual.Window(size=[400, 300], pos=[600, 200], screen=0,
                         allowGUI=False)

Note winL is bigger than winR.

Draw everything in winR and winR is flipped before anything is drawn to winL, then draw everything in winL and flip winL: No problems at all. Similarly, if you do all of winL and then all of winR, no problems. Order of creation doesn’t seem to matter.

Draw everything in winR, draw anything in winL, and then flip winR, then draw everything else in winL and flip winL: Everything drawn in winL AFTER winR is flipped is scaled to winR’s size, i.e., shrunk. Strangely, the thing drawn before winR is flipped is correctly scaled to winL! If you flip winR and winL here (draw everything to winL, draw anything from winR, then flip winL), you get the same effect in winR: Everything draw before winL is flipped is correctly scaled, everything drawn after is expanded to winL’s scale.

Draw everything to winL AND everything in winR, then flip winL and then flip winR: They swap scales. Everything drawn in winL is scaled to winR and vice-versa.

Draw nothing in winR, draw everything in winL, flip winL, then flip winR: Everything in winL scales to winR after the first flip. This one gets better:

As the last one, but wait 60 frames and then start drawing stuff to winR and flipping it after winL has flipped: Both of them are fine again! It resizes everything in winL to its proper scale, and starts behaving like the first setup I mentioned, where you draw everything in a window and flip it and then draw everything in the other window and flip it.

Doing literally anything with waitBlanking: No effect at all.

@jon or @mdc, do you have any ideas about how to fix this? I at least understand what’s happening now, kind of, but it’s very hard to work around and I have no clue where this could be originating from in the code. PsychoPy’s window class? Pyglet window? Pyglet canvas? Something deeper? Also, with regard to my particular program (PyHab), this seems to also happen when flipping a window with a paused movie object in it. Drawing a paused movie object is like drawing nothing to the window, is basically what’s going on. I might be able to draw some kind of invisible dummy object to keep it from doing this as a temporary fix (this does work, just draw something the color of the background onto the background), but I’d love to develop a proper one.