Open v-ein opened 8 months ago
This issue can be fixed in different ways. First, I don't see any good enough reason for the mvSubmitCallback()
call within set_frame_callback()
. By adding the frame callback to the map immediately, I've been able to fix the issue, and set_frame_callback()
then can be used together with the mutex to schedule a callback for the next frame.
However, thinking more about it... Pretend that we didn't use a mutex but just ran, say, set_frame_callback(2, my_callback)
, and the frame 2 started rendering concurrently with set_frame_callback
. Then it would lose the callback anyway, even though in the caller code, the last frame before set_frame_callback
was 1, not 2. That is, what should set_frame_callback
do if it's called a bit too late and misses the target frame? It might be better to schedule the frame callback right away if the target frame has already passed.
Version of Dear PyGui
Version: 1.10.1 Operating System: Windows 10
My Issue/Question
set_frame_callback
can be used at runtime to set a callback to run at the nearest frame. In fact, it's probably the easiest way to schedule something to run in the handlers thread (e.g. from within another user thread).Here's a piece of code that is supposed to do that:
Notice the use of
dpg.mutex()
- it guarantess that no frame gets rendered while we're still computing the frame number forset_frame_callback
. Without the mutex, this code may run into a problem if the main thread starts rendering a frame between the calls toget_frame_count()
andset_frame_callback()
: the frame number we'll get will be outdated, and the callback won't get called.With the mutex, the code seemingly should run without issues 100% of the time. From the API point of view, at least. In practice, due to the way
set_frame_callback
is implemented, there's a slight chance that the callback won't get called and will be lost. The root cause is that to storenext_frame_callback
and the frame number,set_frame_callback()
schedules one more callback to the handlers thread. If it happens on the edge of a new frame, there's a chance that the "internal" callback gets called too late, past the point where frame callback for this new frame is to be scheduled. Here's exactly how it happens:dpg.mutex()
locks the mutex and blocks the rendering thread.set_frame_callback()
gets called and parses its arguments. Let's say the last frame rendered was X, thenset_frame_callback()
gets called with the frame X+1.set_frame_callback()
callsmvSubmitCallback()
, which adds the internal callback to the queue.set_frame_callback()
then exits back to Python.dpg.mutex()
releases the mutex.mvFrameCallback()
for frame X+1.mvFrameCallback()
doesn't find any callbacks for this frame, and does nothing.set_frame_callback()
finally gets called. It adds a frame callback for frame X+1 tocallbackRegistry->frameCallbacks
, but it's too late. That element will never be fetched again.To Reproduce
Steps to reproduce the behavior:
set_frame_callback()
to do its job.set_frame_callback()
to the dangerous point when it runs immediately before rendering of the next frame starts.set_frame_callback()
is offset even further and has no chance to complete in time. All 100% of callbacks are lost.Expected behavior
No callback loss if upon return from
set_frame_callback
the target frame is yet to be rendered.Screenshots/Video
Standalone, minimal, complete and verifiable example