On 31/12/21 1:31 pm, Michael Clark wrote:
I would like to add these implementation notes to the README because
this is information one cannot easily find. It occurs to me that XFlush
before frames makes a lot more sense than after frames if one thinks
about Nagle and flow control combined with frame pacing. If we have
capacity to render at a constant frame rate with accurate scheduling for
the start of frames, then an XFlush(dpy) marker placed at the start of
the frame will occur at a constant rate, subject to variable render
times, whereas an XFlush(dpy) marker placed at the end of the frame
would have irregular timings needing stats for recovery. I am guessing
these are conversations that folks have already had because it seems to
work on my machine. An XSync(dpy, False) marker for congestion control
also seems to make sense to me because if we get frame drops we want to
resynchronize input and output. I am not sure under which conditions one
may wish to do XSync(dpy, True). Possibly some sort of watchdog or hang
check for IO when recovering from flooding.
Anyway I don't know where to go for this information so I am verbalizing
it to see if anyone can acknowledge it as being reasonable protocol.
I wrote up the sample implementation notes here:
- https://github.com/michaeljclark/glxsync/blob/trunk/README.md
I made one minor change after reasoning about when the frame buffer is
volatile. It seemed safe to move the start of the lock from just before
draw_frame to just before swap_buffers. The notes on buffer safety and
waiting until present timing has been received before starting a new
frame are from observations with the Mesa Intel driver in Ubuntu 21.04.
My account of events for a synchronized window update look like this:
- _ClientMessage_
- _synchronize_ (`_e.xclient.data.l[0] == _NET_WM_SYNC_REQUEST`)
- store synchronizatized rendering serial number:
- `sync_serial = _e.xclient.data.l[2]`
- _ConfigureNotify_
- _resize_
- signal synchronizatized rendering initiated:
- `sync_counter(dpy, extended_counter, sync_serial + 0)`
- _Expose_
- _XFlush_
- make sure last frame has been flushed.
- _draw_frame_
- run user code to draw the frame.
- _begin_frame_
- signal buffer contents for serial number are rendering (urgent):
- `sync_counter(dpy, extended_counter, sync_serial + 3)`
- _swap_buffers_
- the frame buffer contents are volatile at this point.
- _end_frame_
- signal buffer contents for serial number are now complete:
- `sync_counter(dpy, extended_counter, sync_serial + 4)`
- _ClientMessage_
- _frame_drawn_ (`e.xclient.message_type == _NET_WM_FRAME_DRAWN`)
- drawing at this point is *not safe* because present has
not been scheduled (buffer has not been copied).
- _ClientMessage_
- _frame_timings_ (`e.xclient.message_type == _NET_WM_FRAME_TIMINGS`)
- drawing at this point is *safe* frame because present has
been scheduled (buffer has been copied).