diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h index 331e71d26..6d80b3492 100644 --- a/src/dearpygui_commands.h +++ b/src/dearpygui_commands.h @@ -2519,8 +2519,9 @@ setup_dearpygui(PyObject* self, PyObject* args, PyObject* kwargs) return nullptr; } - while (!GContext->itemRegistry->containers.empty()) - GContext->itemRegistry->containers.pop(); + // Clear the containers stack. Unfortunately std::stack doesn't have a clear() call, + // but assigning a new empty stack does just the same. + mvItemRegistry::threadContext.containers = {}; GContext->started = true; GContext->running = true; GContext->future = std::async(std::launch::async, []() {return mvRunCallbacks(); }); @@ -2924,18 +2925,19 @@ pop_container_stack(PyObject* self, PyObject* args, PyObject* kwargs) mvPySafeLockGuard lk(GContext->mutex); - if (GContext->itemRegistry->containers.empty()) + auto& containers = mvItemRegistry::threadContext.containers; + if (containers.empty()) { mvThrowPythonError(mvErrorCode::mvContainerStackEmpty, "No container to pop."); assert(false); return nullptr; } - mvAppItem* item = GContext->itemRegistry->containers.top(); - GContext->itemRegistry->containers.pop(); + mvAppItem* item = containers.top(); + containers.pop(); if (item) - return ToPyUUID(item->uuid); + return ToPyUUID(item); else return GetPyNone(); @@ -2945,8 +2947,9 @@ static PyObject* empty_container_stack(PyObject* self, PyObject* args, PyObject* kwargs) { mvPySafeLockGuard lk(GContext->mutex); - while (!GContext->itemRegistry->containers.empty()) - GContext->itemRegistry->containers.pop(); + // Clear the containers stack. Unfortunately std::stack doesn't have a clear() call, + // but assigning a new empty stack does just the same. + mvItemRegistry::threadContext.containers = {}; return GetPyNone(); } @@ -2956,11 +2959,12 @@ top_container_stack(PyObject* self, PyObject* args, PyObject* kwargs) mvPySafeLockGuard lk(GContext->mutex); mvAppItem* item = nullptr; - if (!GContext->itemRegistry->containers.empty()) - item = GContext->itemRegistry->containers.top(); + auto& containers = mvItemRegistry::threadContext.containers; + if (!containers.empty()) + item = containers.top(); if (item) - return ToPyUUID(item->uuid); + return ToPyUUID(item); else return GetPyNone(); } @@ -2970,7 +2974,7 @@ last_item(PyObject* self, PyObject* args, PyObject* kwargs) { mvPySafeLockGuard lk(GContext->mutex); - return ToPyUUID(GContext->itemRegistry->lastItemAdded); + return ToPyUUID(mvItemRegistry::threadContext.lastItemAdded); } static PyObject* @@ -2978,7 +2982,7 @@ last_container(PyObject* self, PyObject* args, PyObject* kwargs) { mvPySafeLockGuard lk(GContext->mutex); - return ToPyUUID(GContext->itemRegistry->lastContainerAdded); + return ToPyUUID(mvItemRegistry::threadContext.lastContainerAdded); } static PyObject* @@ -2986,7 +2990,7 @@ last_root(PyObject* self, PyObject* args, PyObject* kwargs) { mvPySafeLockGuard lk(GContext->mutex); - return ToPyUUID(GContext->itemRegistry->lastRootAdded); + return ToPyUUID(mvItemRegistry::threadContext.lastRootAdded); } static PyObject* @@ -3006,7 +3010,7 @@ push_container_stack(PyObject* self, PyObject* args, PyObject* kwargs) { if (DearPyGui::GetEntityDesciptionFlags(parent->type) & MV_ITEM_DESC_CONTAINER) { - GContext->itemRegistry->containers.push(parent); + mvItemRegistry::threadContext.containers.push(parent); return ToPyBool(true); } } @@ -4164,8 +4168,8 @@ capture_next_item(PyObject* self, PyObject* args, PyObject* kwargs) mvPySafeLockGuard lk(GContext->mutex); - GContext->itemRegistry->captureCallback = mvPyObject(callable == Py_None? nullptr : callable, true); - GContext->itemRegistry->captureCallbackUserData = mvPyObject(user_data, true); + mvItemRegistry::threadContext.captureCallback = mvPyObject(callable == Py_None? nullptr : callable, true); + mvItemRegistry::threadContext.captureCallbackUserData = mvPyObject(user_data, true); return GetPyNone(); } diff --git a/src/mvContext.h b/src/mvContext.h index 5054c62d1..14333b6ea 100644 --- a/src/mvContext.h +++ b/src/mvContext.h @@ -115,7 +115,7 @@ struct mvContext double time = 0.0; // total time since starting int frame = 0; // frame count int framerate = 0; // frame rate - mvUUID id = MV_START_UUID; // current ID + std::atomic id = MV_START_UUID; // current ID mvViewport* viewport = nullptr; mvGraphics graphics; bool resetTheme = false; diff --git a/src/mvItemRegistry.cpp b/src/mvItemRegistry.cpp index 6176ca9c4..1b481a66d 100644 --- a/src/mvItemRegistry.cpp +++ b/src/mvItemRegistry.cpp @@ -12,6 +12,8 @@ #include "mvProfiler.h" +thread_local mvThreadContext mvItemRegistry::threadContext; + mvItemRegistry::mvItemRegistry() { // We seldom need to do a GetItem(0), and an explicit check for uuid == 0 in GetItem @@ -36,17 +38,12 @@ DebugItem(const char* label, const char* item) { ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "%s", item); } -static void -PushParent(mvItemRegistry& registry, mvAppItem* item) -{ - registry.containers.push(item); -} - static mvAppItem* TopParent(mvItemRegistry& registry) { - if (!registry.containers.empty()) - return registry.containers.top(); + auto& containers = mvItemRegistry::threadContext.containers; + if (!containers.empty()) + return containers.top(); return nullptr; } @@ -328,9 +325,10 @@ DeleteItem(mvItemRegistry& registry, mvUUID uuid, b8 childrenOnly, i32 slot) } // See if it is the captured item - if (registry.capturedItem && registry.capturedItem.get() == item) + auto& capturedItem = mvItemRegistry::threadContext.capturedItem; + if (capturedItem && capturedItem.get() == item) { - registry.capturedItem = nullptr; + capturedItem = nullptr; return true; } @@ -352,10 +350,11 @@ MoveItem(mvItemRegistry& registry, mvUUID uuid, mvUUID parent, mvUUID before) std::shared_ptr child = nullptr; - if (registry.capturedItem && registry.capturedItem->uuid == uuid) + auto& capturedItem = mvItemRegistry::threadContext.capturedItem; + if (capturedItem && capturedItem->uuid == uuid) { - child = registry.capturedItem; - registry.capturedItem = nullptr; + child = capturedItem; + capturedItem = nullptr; } else { @@ -652,8 +651,9 @@ mvAppItem* GetItem(mvItemRegistry& registry, mvUUID uuid) { // check captured - if (registry.capturedItem && registry.capturedItem->uuid == uuid) - return registry.capturedItem.get(); + auto& capturedItem = mvItemRegistry::threadContext.capturedItem; + if (capturedItem && capturedItem->uuid == uuid) + return capturedItem.get(); auto found = registry.allItems.find(uuid); return found != registry.allItems.end()? found->second : nullptr; @@ -706,16 +706,18 @@ b8 AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ptr item, mvUUID parent, mvUUID before) { - if(registry.captureCallback) + auto& threadContext = mvItemRegistry::threadContext; + + if (threadContext.captureCallback) { // this is a unique situation in that the caller always has the GIL - registry.capturedItem = item; + threadContext.capturedItem = item; // resetting captureCallback in advance in order to avoid recursion (if the callback // attempts to add another item or move an item) - mvPyObject captureCallback(std::move(registry.captureCallback)); - registry.captureCallback = nullptr; - mvRunCallback(captureCallback, nullptr, registry.capturedItem->uuid); + mvPyObject captureCallback(std::move(threadContext.captureCallback)); + threadContext.captureCallback = nullptr; + mvRunCallback(captureCallback, nullptr, threadContext.capturedItem->uuid); return true; } @@ -733,13 +735,13 @@ AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ptr it //--------------------------------------------------------------------------- if (DearPyGui::GetEntityDesciptionFlags(item->type) & MV_ITEM_DESC_ROOT) { - registry.lastRootAdded = item->uuid; - registry.lastContainerAdded = item->uuid; + threadContext.lastRootAdded = item->uuid; + threadContext.lastContainerAdded = item->uuid; } else if (DearPyGui::GetEntityDesciptionFlags(item->type) & MV_ITEM_DESC_CONTAINER) - registry.lastContainerAdded = item->uuid; + threadContext.lastContainerAdded = item->uuid; - registry.lastItemAdded = item->uuid; + threadContext.lastItemAdded = item->uuid; //--------------------------------------------------------------------------- // STEP 1: check if an item with this name exists (NO LONGER NEEDED) diff --git a/src/mvItemRegistry.h b/src/mvItemRegistry.h index 71d830049..09e83921b 100644 --- a/src/mvItemRegistry.h +++ b/src/mvItemRegistry.h @@ -48,6 +48,25 @@ b8 AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ void ResetTheme (mvItemRegistry& registry); b8 ReorderChildren (mvItemRegistry& registry, mvUUID parent, i32 slot, const std::vector& new_order); +//----------------------------------------------------------------------------- +// mvThreadContext +// - Holds thread-local data used together with mvItemRegistry +//----------------------------------------------------------------------------- + +struct mvThreadContext +{ + // "last item" state + mvUUID lastItemAdded = 0; + mvUUID lastContainerAdded = 0; + mvUUID lastRootAdded = 0; + + std::stack containers; // parent stack, top of stack becomes widget's parent + + std::shared_ptr capturedItem = nullptr; + mvPyObject captureCallback = nullptr; + mvPyObject captureCallbackUserData = nullptr; +}; + //----------------------------------------------------------------------------- // mvItemRegistry // - Responsibilities: @@ -61,21 +80,16 @@ b8 ReorderChildren (mvItemRegistry& registry, mvUUID paren struct mvItemRegistry { - // "last item" state - mvUUID lastItemAdded = 0; - mvUUID lastContainerAdded = 0; - mvUUID lastRootAdded = 0; - // misc - std::stack containers; // parent stack, top of stack becomes widget's parent std::unordered_map aliases; std::unordered_map allItems; // used for quick access to items by UUID + + static thread_local mvThreadContext threadContext; + + // debug items b8 showImGuiDebug = false; b8 showImPlotDebug = false; - std::vector> debugWindows; - std::shared_ptr capturedItem = nullptr; - mvPyObject captureCallback = nullptr; - mvPyObject captureCallbackUserData = nullptr; + std::vector> debugWindows; // roots std::vector> colormapRoots;