Skip to content

Commit 2362735

Browse files
v-einhoffstadt
authored andcommitted
fix: A better version of split_frame, releases immediately after frame rendering has ended.
1 parent e24a8f2 commit 2362735

File tree

7 files changed

+61
-31
lines changed

7 files changed

+61
-31
lines changed

dearpygui/_dearpygui.pyi

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/_dearpygui_RTD.py

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/dearpygui.py

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dearpygui_commands.h

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,19 +2271,26 @@ save_init_file(PyObject* self, PyObject* args, PyObject* kwargs)
22712271
static PyObject*
22722272
split_frame(PyObject* self, PyObject* args, PyObject* kwargs)
22732273
{
2274-
i32 delay = 32;
2275-
2276-
if (!Parse((GetParsers())["split_frame"], args, kwargs, __FUNCTION__,
2277-
&delay))
2274+
if (!Parse((GetParsers())["split_frame"], args, kwargs, __FUNCTION__))
22782275
return GetPyNone();
22792276

2280-
// std::lock_guard<std::recursive_mutex> lk(GContext->mutex);
2277+
if (GContext->running)
2278+
{
2279+
Py_BEGIN_ALLOW_THREADS;
2280+
std::unique_lock lk(GContext->frameEndedMutex);
2281+
GContext->frameEnded = false;
2282+
GContext->frameEndedEvent.wait(lk, []{return GContext->frameEnded;});
2283+
lk.unlock();
22812284

2282-
Py_BEGIN_ALLOW_THREADS;
2283-
GContext->waitOneFrame = true;
2284-
while (GContext->waitOneFrame)
2285-
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
2286-
Py_END_ALLOW_THREADS;
2285+
Py_END_ALLOW_THREADS;
2286+
}
2287+
2288+
// Now let's see if it was successful (there's a chance that DPG got stopped while we were waiting)
2289+
if (!GContext->running)
2290+
{
2291+
mvThrowPythonError(mvErrorCode::mvNone, "split_frame is exiting: there is no active rendering loop.");
2292+
return nullptr;
2293+
}
22872294

22882295
return GetPyNone();
22892296
}
@@ -2654,7 +2661,7 @@ destroy_context(PyObject* self, PyObject* args, PyObject* kwargs)
26542661
else
26552662
{
26562663
// Make sure everyone knows we're shutting down, even if stop_dearpygui
2657-
// was not called.
2664+
// was not called. This also releases any waiting split_frame calls.
26582665
StopRendering();
26592666

26602667
Py_BEGIN_ALLOW_THREADS;

src/dearpygui_parsers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ InsertParser_Block1(std::map<std::string, mvPythonParser>& parsers)
564564

565565
{
566566
std::vector<mvPythonDataElement> args;
567-
args.push_back({ mvPyDataType::Integer, "delay", mvArgType::KEYWORD_ARG, "32", "Minimal delay in in milliseconds" });
567+
args.push_back({ mvPyDataType::Integer, "delay", mvArgType::DEPRECATED_REMOVE_KEYWORD_ARG, "32", "Do not use it anymore, it has no effect." });
568568

569569
mvPythonParserSetup setup;
570570
setup.about = "Waits one frame.";

src/mvContext.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,22 @@ Render()
182182

183183
mvToolManager::Draw();
184184

185+
if (GContext->resetTheme)
185186
{
186-
if (GContext->resetTheme)
187-
{
188-
SetDefaultTheme();
189-
GContext->resetTheme = false;
190-
}
191-
192-
mvRunTasks();
193-
RenderItemRegistry(*GContext->itemRegistry);
194-
mvRunTasks();
187+
SetDefaultTheme();
188+
GContext->resetTheme = false;
195189
}
196190

197-
if (GContext->waitOneFrame == true)
198-
GContext->waitOneFrame = false;
191+
mvRunTasks();
192+
RenderItemRegistry(*GContext->itemRegistry);
193+
mvRunTasks();
194+
195+
// release split_frame if it's waiting for the frame end
196+
{
197+
std::lock_guard lk(GContext->frameEndedMutex);
198+
GContext->frameEnded = true;
199+
}
200+
GContext->frameEndedEvent.notify_all();
199201
}
200202

201203
std::map<std::string, mvPythonParser>&
@@ -206,7 +208,20 @@ GetParsers()
206208

207209
void StopRendering()
208210
{
211+
// While it may seem reasonable to set it to false with frameEndedMutex locked
212+
// (to set it simultaneously with frameEnded), we don't want to spur another
213+
// race condition between split_frame() and is_dearpygui_running() in the
214+
// rendering loop. Let's trigger it as early as possible.
209215
GContext->running = false;
216+
217+
// Unblock handlers (or other code) that might be waiting in `split_frame()`
218+
{
219+
std::lock_guard lk(GContext->frameEndedMutex);
220+
// Simulate an end of frame even though there's no real frame this time.
221+
// Otherwise split_frame will continue waiting.
222+
GContext->frameEnded = true;
223+
}
224+
GContext->frameEndedEvent.notify_all();
210225
}
211226

212227
void

src/mvContext.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ struct mvIO
102102

103103
struct mvContext
104104
{
105-
std::atomic_bool waitOneFrame = false;
105+
std::mutex frameEndedMutex;
106+
std::condition_variable frameEndedEvent;
107+
bool frameEnded = false;
106108
// Indicates whether DPG has started at least once in this context, i.e. whether
107109
// associated Dear ImGui contexts exist and can be read from.
108110
std::atomic_bool started = false;

0 commit comments

Comments
 (0)