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).

Reply via email to