From fc74466c9ba50598e2fdcb2b034b41ab499f314e Mon Sep 17 00:00:00 2001 From: denvoros Date: Wed, 22 Oct 2025 09:52:58 -0600 Subject: [PATCH 1/3] feat: added white screen begin detector (useful for other random projects) --- .../CommonTools/Async/InferenceRoutines.cpp | 6 ++--- .../VisualDetectors/BlackScreenDetector.cpp | 25 +++++++++++++++++++ .../VisualDetectors/BlackScreenDetector.h | 23 +++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp b/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp index 2c9d9a0a11..829bf24f3a 100644 --- a/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp +++ b/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp @@ -11,9 +11,9 @@ #include "InferenceSession.h" #include "InferenceRoutines.h" -//#include -//using std::cout; -//using std::endl; +// #include +// using std::cout; +// using std::endl; namespace PokemonAutomation{ diff --git a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp index d4d919960f..79e5b671bf 100644 --- a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp +++ b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp @@ -122,7 +122,32 @@ bool WhiteScreenOverWatcher::white_is_over(const ImageViewRGB32& frame){ } +WhiteScreenBeginWatcher::WhiteScreenBeginWatcher( + Color color, const ImageFloatBox& box, + double min_rgb_sum, + double max_stddev_sum +) + : VisualInferenceCallback("WhiteScreenBeginWatcher") + , m_detector(color, box, min_rgb_sum, max_stddev_sum) +{} +void WhiteScreenBeginWatcher::make_overlays(VideoOverlaySet& items) const{ + m_detector.make_overlays(items); +} +bool WhiteScreenBeginWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp){ + return white_has_begun(frame); +} +bool WhiteScreenBeginWatcher::white_has_begun(const ImageViewRGB32& frame){ + if (m_triggered) { + return false; + } + + if (m_detector.detect(frame)){ + m_triggered = true; + return true; + } + return false; +} diff --git a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h index 12c99b2a01..f246d9d36e 100644 --- a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h +++ b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h @@ -122,5 +122,28 @@ class WhiteScreenOverWatcher : public VisualInferenceCallback{ + +class WhiteScreenBeginWatcher : public VisualInferenceCallback{ +public: + WhiteScreenBeginWatcher( + Color color = COLOR_RED, + const ImageFloatBox& box = {0.1, 0.1, 0.8, 0.8}, + double min_rgb_sum = 500, + double max_stddev_sum = 10 + ); + + bool white_has_begun(const ImageViewRGB32& frame); + + virtual void make_overlays(VideoOverlaySet& items) const override; + + virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override; + +private: + WhiteScreenDetector m_detector; + bool m_triggered = false; +}; + + + } #endif From 10efe3f0f6c7196e18c0e4d50d4fee17b1c46787 Mon Sep 17 00:00:00 2001 From: denvoros Date: Fri, 7 Nov 2025 14:46:01 -0700 Subject: [PATCH 2/3] fix: fix segfault crash (at least on linux) by force stopping thread pools before program exist instead of in forced cleanup --- .../Cpp/Concurrency/ComputationThreadPool.cpp | 5 +++- .../Cpp/Concurrency/ComputationThreadPool.h | 2 ++ .../Concurrency/ComputationThreadPoolCore.cpp | 23 +++++++++++++++++-- .../Concurrency/ComputationThreadPoolCore.h | 2 ++ .../Cpp/Concurrency/FireForgetDispatcher.cpp | 10 +++++++- Common/Cpp/Concurrency/FireForgetDispatcher.h | 2 ++ Common/Cpp/Concurrency/Thread.cpp | 4 ++++ Common/Cpp/Concurrency/Thread.h | 1 + .../Source/CommonFramework/Main.cpp | 8 +++++++ 9 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Common/Cpp/Concurrency/ComputationThreadPool.cpp b/Common/Cpp/Concurrency/ComputationThreadPool.cpp index 70b0774dc0..7806b62f5e 100644 --- a/Common/Cpp/Concurrency/ComputationThreadPool.cpp +++ b/Common/Cpp/Concurrency/ComputationThreadPool.cpp @@ -64,7 +64,10 @@ void ComputationThreadPool::run_in_parallel( ){ m_core->run_in_parallel(func, start, end, block_size); } - +void ComputationThreadPool::stop() { + // force call stop in computation thread pool + m_core->stop(); +} diff --git a/Common/Cpp/Concurrency/ComputationThreadPool.h b/Common/Cpp/Concurrency/ComputationThreadPool.h index b71194dbd0..6c5d4f82f3 100644 --- a/Common/Cpp/Concurrency/ComputationThreadPool.h +++ b/Common/Cpp/Concurrency/ComputationThreadPool.h @@ -41,6 +41,8 @@ class ComputationThreadPool final{ void ensure_threads(size_t threads); + void stop(); + // void wait_for_everything(); diff --git a/Common/Cpp/Concurrency/ComputationThreadPoolCore.cpp b/Common/Cpp/Concurrency/ComputationThreadPoolCore.cpp index 54c5f45be6..05b03adf49 100644 --- a/Common/Cpp/Concurrency/ComputationThreadPoolCore.cpp +++ b/Common/Cpp/Concurrency/ComputationThreadPoolCore.cpp @@ -31,21 +31,40 @@ ComputationThreadPoolCore::ComputationThreadPoolCore( spawn_thread(); } } -ComputationThreadPoolCore::~ComputationThreadPoolCore(){ + +void ComputationThreadPoolCore::stop() { { std::lock_guard lg(m_lock); + if (m_stopping) return; m_stopping = true; m_thread_cv.notify_all(); // m_dispatch_cv.notify_all(); } for (ThreadData& thread : m_threads){ - thread.thread.join(); + if (thread.thread.joinable()) { + thread.thread.join(); + } } + + // DO NOT JOIN AGAIN IN DESTRUCTOR + m_threads.clear(); + for (auto& task : m_queue){ task->report_cancelled(); } + + // DO NOT CLEAR AGAIN IN DESTRUCTOR + m_queue.clear(); + } +ComputationThreadPoolCore::~ComputationThreadPoolCore(){ + stop(); +} + + + + WallDuration ComputationThreadPoolCore::cpu_time() const{ // TODO: Don't lock the entire queue. WallDuration ret = WallDuration::zero(); diff --git a/Common/Cpp/Concurrency/ComputationThreadPoolCore.h b/Common/Cpp/Concurrency/ComputationThreadPoolCore.h index c72a71e24e..c69312f517 100644 --- a/Common/Cpp/Concurrency/ComputationThreadPoolCore.h +++ b/Common/Cpp/Concurrency/ComputationThreadPoolCore.h @@ -47,6 +47,8 @@ class ComputationThreadPoolCore final{ WallDuration cpu_time() const; void ensure_threads(size_t threads); + + void stop(); // void wait_for_everything(); diff --git a/Common/Cpp/Concurrency/FireForgetDispatcher.cpp b/Common/Cpp/Concurrency/FireForgetDispatcher.cpp index a796ae00b4..09cac78d08 100644 --- a/Common/Cpp/Concurrency/FireForgetDispatcher.cpp +++ b/Common/Cpp/Concurrency/FireForgetDispatcher.cpp @@ -22,14 +22,22 @@ FireForgetDispatcher global_dispatcher; FireForgetDispatcher::FireForgetDispatcher() : m_stopping(false) {} -FireForgetDispatcher::~FireForgetDispatcher(){ + +void FireForgetDispatcher::stop() { { std::lock_guard lg(m_lock); + // like with the other thread option, make sure we're not double stopping + if (m_stopping) return; m_stopping = true; m_cv.notify_all(); } m_thread.join(); } + +FireForgetDispatcher::~FireForgetDispatcher(){ + stop(); +} + void FireForgetDispatcher::dispatch(std::function&& func){ std::lock_guard lg(m_lock); m_queue.emplace_back(std::move(func)); diff --git a/Common/Cpp/Concurrency/FireForgetDispatcher.h b/Common/Cpp/Concurrency/FireForgetDispatcher.h index 4fbc45af92..d621a6e9a0 100644 --- a/Common/Cpp/Concurrency/FireForgetDispatcher.h +++ b/Common/Cpp/Concurrency/FireForgetDispatcher.h @@ -25,6 +25,8 @@ class FireForgetDispatcher{ // Call "handle->wait()" to wait for the task to finish. void dispatch(std::function&& func); + void stop(); + private: void thread_loop(); diff --git a/Common/Cpp/Concurrency/Thread.cpp b/Common/Cpp/Concurrency/Thread.cpp index c22342d2ab..4329480e8e 100644 --- a/Common/Cpp/Concurrency/Thread.cpp +++ b/Common/Cpp/Concurrency/Thread.cpp @@ -82,6 +82,10 @@ void Thread::join(){ } +bool Thread::joinable() const{ + return m_data && m_data->m_thread.joinable(); +} + } diff --git a/Common/Cpp/Concurrency/Thread.h b/Common/Cpp/Concurrency/Thread.h index e0b9e8bdb2..edbad47ccf 100644 --- a/Common/Cpp/Concurrency/Thread.h +++ b/Common/Cpp/Concurrency/Thread.h @@ -35,6 +35,7 @@ class Thread{ return m_data; } void join(); + bool joinable() const; private: enum class State; diff --git a/SerialPrograms/Source/CommonFramework/Main.cpp b/SerialPrograms/Source/CommonFramework/Main.cpp index 1668f3a33c..183d2e8ae0 100644 --- a/SerialPrograms/Source/CommonFramework/Main.cpp +++ b/SerialPrograms/Source/CommonFramework/Main.cpp @@ -5,8 +5,10 @@ //#include #include #include "Common/Cpp/Concurrency/AsyncTask.h" +#include "Common/Cpp/Concurrency/FireForgetDispatcher.h" #include "Common/Cpp/Exceptions.h" #include "Common/Cpp/ImageResolution.h" +#include "CommonFramework/Tools/GlobalThreadPools.h" #include "Globals.h" #include "GlobalSettingsPanel.h" #include "PersistentSettings.h" @@ -149,6 +151,12 @@ int main(int argc, char *argv[]){ Integration::DppClient::Client::instance().disconnect(); #endif + // Force stop the thread pool + PokemonAutomation::GlobalThreadPools::realtime_inference().stop(); + PokemonAutomation::GlobalThreadPools::normal_inference().stop(); + + PokemonAutomation::global_dispatcher.stop(); + // We must clear the OCR cache or it will crash on Linux when the library // unloads before the cache is destructed from static memory. OCR::clear_cache(); From 5b487d07ecc554f2057f29e2d06df897f3b6c80a Mon Sep 17 00:00:00 2001 From: denvoros Date: Fri, 7 Nov 2025 14:59:30 -0700 Subject: [PATCH 3/3] Revert "feat: added white screen begin detector (useful for other random projects)" This reverts commit fc74466c9ba50598e2fdcb2b034b41ab499f314e. --- .../CommonTools/Async/InferenceRoutines.cpp | 6 ++--- .../VisualDetectors/BlackScreenDetector.cpp | 25 ------------------- .../VisualDetectors/BlackScreenDetector.h | 23 ----------------- 3 files changed, 3 insertions(+), 51 deletions(-) diff --git a/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp b/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp index 829bf24f3a..2c9d9a0a11 100644 --- a/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp +++ b/SerialPrograms/Source/CommonTools/Async/InferenceRoutines.cpp @@ -11,9 +11,9 @@ #include "InferenceSession.h" #include "InferenceRoutines.h" -// #include -// using std::cout; -// using std::endl; +//#include +//using std::cout; +//using std::endl; namespace PokemonAutomation{ diff --git a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp index 79e5b671bf..d4d919960f 100644 --- a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp +++ b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.cpp @@ -122,32 +122,7 @@ bool WhiteScreenOverWatcher::white_is_over(const ImageViewRGB32& frame){ } -WhiteScreenBeginWatcher::WhiteScreenBeginWatcher( - Color color, const ImageFloatBox& box, - double min_rgb_sum, - double max_stddev_sum -) - : VisualInferenceCallback("WhiteScreenBeginWatcher") - , m_detector(color, box, min_rgb_sum, max_stddev_sum) -{} -void WhiteScreenBeginWatcher::make_overlays(VideoOverlaySet& items) const{ - m_detector.make_overlays(items); -} -bool WhiteScreenBeginWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp){ - return white_has_begun(frame); -} -bool WhiteScreenBeginWatcher::white_has_begun(const ImageViewRGB32& frame){ - if (m_triggered) { - return false; - } - - if (m_detector.detect(frame)){ - m_triggered = true; - return true; - } - return false; -} diff --git a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h index f246d9d36e..12c99b2a01 100644 --- a/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h +++ b/SerialPrograms/Source/CommonTools/VisualDetectors/BlackScreenDetector.h @@ -122,28 +122,5 @@ class WhiteScreenOverWatcher : public VisualInferenceCallback{ - -class WhiteScreenBeginWatcher : public VisualInferenceCallback{ -public: - WhiteScreenBeginWatcher( - Color color = COLOR_RED, - const ImageFloatBox& box = {0.1, 0.1, 0.8, 0.8}, - double min_rgb_sum = 500, - double max_stddev_sum = 10 - ); - - bool white_has_begun(const ImageViewRGB32& frame); - - virtual void make_overlays(VideoOverlaySet& items) const override; - - virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override; - -private: - WhiteScreenDetector m_detector; - bool m_triggered = false; -}; - - - } #endif