Alternating between two images for each frame, unavoidable flicker?

This is a simplified code snippet to show the issue (epilepsy warning):

from panda3d.core import *

loadPrcFileData("", "fullscreen #t")
loadPrcFileData("", "sync-video #t")
loadPrcFileData("", "show-frame-rate-meter #t")

from direct.showbase.ShowBase import ShowBase
from direct.task import Task

base = ShowBase()

state = 0

cm1 = CardMaker('card')
cm1.setFrame(-1, 1, -1, 1)
redcard = NodePath(cm1.generate())
redcard.setColor(1,0,0,1)
redcard.reparentTo(base.render2d)
redcard.hide()

cm2 = CardMaker('card')
cm2.setFrame(-1, 1, -1, 1)
bluecard = NodePath(cm2.generate())
bluecard.setColor(0,0,1,1)
bluecard.reparentTo(base.render2d)
bluecard.hide()

def myTaskFunc(task):
    global state
    
    if state == 0:
        bluecard.show()
        redcard.hide()
        state = 1
    elif state == 1:
        redcard.show()
        bluecard.hide()
        state = 0
    
    return task.cont

myTask = base.taskMgr.add(myTaskFunc, 'myTask')

base.run()

It should show red one frame, blue the next, repeat. Due to persistence of vision, you should be seeing violet.
The issue is once every 5 seconds or so, you get red or blue frames reused at least once, which results in a flicker/popping artifact due to displaying two red or two blue frames in a sequence. Even when vsync is on.

The real code I have is for a device which does optical pixel shifting. My program syncs with the optical pixel shifter, and Panda provides only even or odd rows of pixels each frame.

I’d be very surprised if this was normal behavior even when vsync is on, as any 3d camera movement would produce noticeable jitter due to some frames being reused, and more specific uses like VR would become nauseating due to reused frames when you move your head around.

The FPS meter never shows true 60 or 120 fps, it is always off by 0.1 or less. I had always assumed it was just a bug/limitation of the fps counter, but maybe it shows the issue that is causing this problem.

def myTaskFunc(task):
    global state
    
    if state == 0:
        bluecard.show()
        redcard.hide()
        state = 1
    elif state == 1:
        redcard.show()
        bluecard.hide()
        state = 0

    base.screenshot()
    return task.cont

I added a screenshot function to the task, and I could not detect out of 760 pictures taken twice of the same color, they all alternate.

Perhaps you do not take into account that the real frequency of the monitor may fluctuate and the effect of the straboscope is created.

Please note that liquid-crystal display have a delay.

base.screenshot() is slow. I think what you’re doing is forcing the program to run very slow, so the issue doesn’t happen then.

Perhaps you do not take into account that the real frequency of the monitor may fluctuate

I don’t think that’s how monitors work. The electronics and physics of the display tech is very precise, much more than the few milliseconds a single frame lasts (for example, the liquid crystal material itself switches from state 100% to state 0% in at most 3 milliseconds and <=1 millisecond the other way, the LED backlight PWM control allows it to switch at sub-millisecond rates, the video signals in the electronics are at the nano to micro-second precision). If this wasn’t the case, you’d see stuttering and jumps even on normal simple scenes when vsync was enabled.

This probably isn’t helpful, but when I run your provided code, I see a constant violet screen at 144.0 FPS on the dot. I do not notice any popping or artifacts.

1 Like

It is helpful and much appreciated as you’re providing testing feedback.
If you don’t mind, can you test it some more? Specifically,

  1. How long did you leave it running? You’ll need more than few seconds to start seeing stuff.
  2. Was it fullscreen or windowed on your end?
  3. Do you notice a trailing red artifact when moving the mouse around? Or have you also disabled the mouse cursor?
  4. Is it possible for you to run it when the monitor is set to 60Hz? 144Hz is so high that any popping artifact may not be very noticeable.
  5. What’s your CPU and GPU specs?
  6. Do you have the exact same experience when running something like another game in the background?
  7. When you say “144.0 FPS on the dot”, do you mean your Panda3D frame counter always provides round numbers for the fps? I’m not sure I’ve experienced that ever.

Much appreciated in any case.

I can see the flicker if I run the program in a window, but in fullscreen at my display’s native 144.00 Hz the code produces a seamless violet effect for multiple minutes.

I’m going to suggest this issue is to do with display sync protocols more than Panda3D’s implementation.

The fullscreen thing gives me a clue but it would be much more helpful if you could at least mention your monitor/CPU/GPU. Do you have adaptive sync enabled on your monitor by any chance?