From 7d0f816d117af3a0d4f9f0088e43c4aaa5b63e9d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 6 Sep 2023 04:03:53 -0500 Subject: [PATCH 1/5] prepare for threaded rendering? one chip per thread. --- src/engine/engine.h | 4 ++++ src/engine/playback.cpp | 26 ++++++++++++++++++++------ src/engine/workPool.cpp | 8 +++++--- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 3a5b1b044..ba2cf567b 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -39,6 +39,8 @@ #include #include +class DivWorkPool; + #define addWarning(x) \ if (warnings.empty()) { \ warnings+=x; \ @@ -485,6 +487,8 @@ class DivEngine { size_t totalProcessed; + DivWorkPool* renderPool; + // MIDI stuff std::function midiCallback=[](const TAMidiMessage&) -> int {return -2;}; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 270dd7f79..0b21dc57f 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -22,6 +22,7 @@ #define _USE_MATH_DEFINES #include "dispatch.h" #include "engine.h" +#include "workPool.h" #include "../ta-log.h" #include @@ -1788,6 +1789,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now(); + if (renderPool==NULL) { + // TODO: test this + renderPool=new DivWorkPool(0); + } + // process MIDI events (TODO: everything) if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) { TAMidiMessage& msg=output->midiIn->queue.front(); @@ -2061,20 +2067,28 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi // 5. tick the clock and fill buffers as needed if (cyclespush([this,size](void* d) { + DivDispatchContainer* dc=(DivDispatchContainer*)d; + int total=(cycles*dc->runtotal)/(size<acquire(dc->runPos,total); + dc->runLeft-=total; + dc->runPos+=total; + },&disCont[i]); } + renderPool->wait(); runLeftG-=cycles; cycles=0; } else { cycles-=runLeftG; runLeftG=0; for (int i=0; ipush([](void* d) { + DivDispatchContainer* dc=(DivDispatchContainer*)d; + dc->acquire(dc->runPos,dc->runLeft); + dc->runLeft=0; + },&disCont[i]); } + renderPool->wait(); } } } diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index a62f4ff7a..fa67aeb50 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -50,7 +50,9 @@ void DivWorkThread::run() { task.func(task.funcArg); - parent->busyCount--; + if (--parent->busyCount<0) { + logE("oh no PROBLEM..."); + } parent->notify.notify_one(); } } @@ -125,8 +127,8 @@ void DivWorkPool::wait() { } // wait - while (busyCount!=0) { - if (notify.wait_for(unique,std::chrono::milliseconds(100))==std::cv_status::timeout) { + while (busyCount>0) { + if (notify.wait_for(unique,std::chrono::milliseconds(30))==std::cv_status::timeout) { logW("DivWorkPool: wait() timed out!"); } } From e1cd05e8e9e71b89a478c253e810a47126ca2a09 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 6 Sep 2023 04:06:26 -0500 Subject: [PATCH 2/5] reset workPool position after wait --- src/engine/workPool.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index fa67aeb50..8c187adc6 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -132,6 +132,8 @@ void DivWorkPool::wait() { logW("DivWorkPool: wait() timed out!"); } } + + pos=0; } DivWorkPool::DivWorkPool(unsigned int threads): From e8dbacf6e04d1658c32c34a4b299f7257ea224cc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 6 Sep 2023 06:22:03 -0500 Subject: [PATCH 3/5] this is horrible --- src/engine/workPool.cpp | 36 +++++++++++++++++++++++++++--------- src/engine/workPool.h | 5 +++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index 8c187adc6..b3610f74c 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -21,6 +21,8 @@ #include "../ta-log.h" #include +#include + void* _workThread(void* inst) { ((DivWorkThread*)inst)->run(); return NULL; @@ -29,6 +31,7 @@ void* _workThread(void* inst) { void DivWorkThread::run() { std::unique_lock unique(selfLock); DivPendingTask task; + bool setFuckingPromise=false; logV("running work thread"); @@ -37,7 +40,10 @@ void DivWorkThread::run() { if (tasks.empty()) { lock.unlock(); isBusy=false; - parent->notify.notify_one(); + if (setFuckingPromise) { + parent->notify.set_value(); + setFuckingPromise=false; + } if (terminate) { break; } @@ -50,10 +56,13 @@ void DivWorkThread::run() { task.func(task.funcArg); - if (--parent->busyCount<0) { + int busyCount=--parent->busyCount; + if (busyCount<0) { logE("oh no PROBLEM..."); } - parent->notify.notify_one(); + if (busyCount==0) { + setFuckingPromise=true; + } } } } @@ -119,7 +128,14 @@ bool DivWorkPool::busy() { void DivWorkPool::wait() { if (!threaded) return; - std::unique_lock unique(selfLock); + //std::unique_lock unique(selfLock); + + if (busyCount==0) { + logV("nothing to do"); + return; + } + + std::future future=notify.get_future(); // start running for (unsigned int i=0; i0) { - if (notify.wait_for(unique,std::chrono::milliseconds(30))==std::cv_status::timeout) { - logW("DivWorkPool: wait() timed out!"); - } - } + logV("waiting on future"); + //SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","waiting on future.",NULL); + future.wait(); + //SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","waited - reset promise.",NULL); + + notify=std::promise(); + //SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error","YES",NULL); pos=0; } diff --git a/src/engine/workPool.h b/src/engine/workPool.h index f47711353..aab8a1ea2 100644 --- a/src/engine/workPool.h +++ b/src/engine/workPool.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "fixedQueue.h" @@ -75,7 +76,7 @@ class DivWorkPool { unsigned int pos; DivWorkThread* workThreads; public: - std::condition_variable notify; + std::promise notify; std::atomic busyCount; /** @@ -98,4 +99,4 @@ class DivWorkPool { ~DivWorkPool(); }; -#endif \ No newline at end of file +#endif From 5329e551d4f0395fbdcc18bbc3d930aaa6082826 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 6 Sep 2023 14:23:47 -0500 Subject: [PATCH 4/5] improve a bit and add option --- src/engine/engine.cpp | 6 ++++++ src/engine/engine.h | 3 +++ src/engine/playback.cpp | 3 +-- src/engine/workPool.cpp | 7 +++++-- src/engine/workPool.h | 1 - src/gui/gui.h | 2 ++ src/gui/settings.cpp | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7f2ad1010..a22370da8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -23,6 +23,7 @@ #include "engine.h" #include "instrument.h" #include "safeReader.h" +#include "workPool.h" #include "../ta-log.h" #include "../fileutils.h" #ifdef HAVE_SDL2 @@ -3123,6 +3124,10 @@ bool DivEngine::switchMaster(bool full) { quitDispatch(); initDispatch(); } + if (renderPool!=NULL) { + delete renderPool; + renderPool=NULL; + } if (initAudioBackend()) { for (int i=0; i2.0f) metroVol=2.0f; + renderPoolThreads=getConfInt("renderPoolThreads",0); if (lowLatency) logI("using low latency mode."); diff --git a/src/engine/engine.h b/src/engine/engine.h index ba2cf567b..25add2069 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -487,6 +487,7 @@ class DivEngine { size_t totalProcessed; + unsigned int renderPoolThreads; DivWorkPool* renderPool; // MIDI stuff @@ -1264,6 +1265,8 @@ class DivEngine { metroAmp(0.0f), metroVol(1.0f), totalProcessed(0), + renderPoolThreads(0), + renderPool(NULL), curOrders(NULL), curPat(NULL), tempIns(NULL), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0b21dc57f..5f06d31aa 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1790,8 +1790,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now(); if (renderPool==NULL) { - // TODO: test this - renderPool=new DivWorkPool(0); + renderPool=new DivWorkPool(renderPoolThreads); } // process MIDI events (TODO: everything) diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index b3610f74c..6a562bfe2 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -43,11 +43,14 @@ void DivWorkThread::run() { if (setFuckingPromise) { parent->notify.set_value(); setFuckingPromise=false; + std::this_thread::yield(); } if (terminate) { break; } - notify.wait(unique); + if (notify.wait_for(unique,std::chrono::milliseconds(100))==std::cv_status::timeout) { + logE("this task timed out!"); + } continue; } else { task=tasks.front(); @@ -128,7 +131,6 @@ bool DivWorkPool::busy() { void DivWorkPool::wait() { if (!threaded) return; - //std::unique_lock unique(selfLock); if (busyCount==0) { logV("nothing to do"); @@ -141,6 +143,7 @@ void DivWorkPool::wait() { for (unsigned int i=0; i0); + if (ImGui::Checkbox("Multi-threaded (EXPERIMENTAL)",&renderPoolThreadsB)) { + if (renderPoolThreadsB) { + settings.renderPoolThreads=2; + } else { + settings.renderPoolThreads=0; + } + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("runs chip emulation on separate threads.\nmay increase performance on multi-core CPUs.\n\nwarnings:\n- experimental! currently broken/not working well.\n- only useful on multi-chip songs."); + } + + if (renderPoolThreadsB) { + pushWarningColor(settings.renderPoolThreads>cpuCores,settings.renderPoolThreads>cpuCores); + if (ImGui::InputInt("Number of threads",&settings.renderPoolThreads)) { + if (settings.renderPoolThreads<2) settings.renderPoolThreads=2; + if (settings.renderPoolThreads>32) settings.renderPoolThreads=32; + } + if (settings.renderPoolThreads>=DIV_MAX_CHIPS) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("that's the limit!"); + } + } else if (settings.renderPoolThreads>cpuCores) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("it is a VERY bad idea to set this number higher than your CPU core count (%d)!",cpuCores); + } + } + popWarningColor(); + } + bool lowLatencyB=settings.lowLatency; if (ImGui::Checkbox("Low-latency mode",&lowLatencyB)) { settings.lowLatency=lowLatencyB; @@ -3284,6 +3314,7 @@ void FurnaceGUI::syncSettings() { settings.classicChipOptions=e->getConfInt("classicChipOptions",0); settings.wasapiEx=e->getConfInt("wasapiEx",0); settings.chanOscThreads=e->getConfInt("chanOscThreads",0); + settings.renderPoolThreads=e->getConfInt("renderPoolThreads",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.headFontSize,2,96); @@ -3433,6 +3464,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.classicChipOptions,0,1); clampSetting(settings.wasapiEx,0,1); clampSetting(settings.chanOscThreads,0,256); + clampSetting(settings.renderPoolThreads,0,256); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -3689,6 +3721,7 @@ void FurnaceGUI::commitSettings() { e->setConf("classicChipOptions",settings.classicChipOptions); e->setConf("wasapiEx",settings.wasapiEx); e->setConf("chanOscThreads",settings.chanOscThreads); + e->setConf("renderPoolThreads",settings.renderPoolThreads); // colors for (int i=0; i Date: Wed, 6 Sep 2023 16:39:35 -0500 Subject: [PATCH 5/5] much better --- src/engine/workPool.cpp | 27 +++++++++++++++++++-------- src/engine/workPool.h | 8 ++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/engine/workPool.cpp b/src/engine/workPool.cpp index 6a562bfe2..8772dfba3 100644 --- a/src/engine/workPool.cpp +++ b/src/engine/workPool.cpp @@ -29,7 +29,7 @@ void* _workThread(void* inst) { } void DivWorkThread::run() { - std::unique_lock unique(selfLock); + //std::unique_lock unique(selfLock); DivPendingTask task; bool setFuckingPromise=false; @@ -48,9 +48,12 @@ void DivWorkThread::run() { if (terminate) { break; } - if (notify.wait_for(unique,std::chrono::milliseconds(100))==std::cv_status::timeout) { - logE("this task timed out!"); - } + std::future future=notify.get_future(); + future.wait(); + lock.lock(); + notify=std::promise(); + promiseAlreadySet=false; + lock.unlock(); continue; } else { task=tasks.front(); @@ -94,8 +97,8 @@ bool DivWorkThread::busy() { void DivWorkThread::finish() { lock.lock(); terminate=true; + notify.set_value(); lock.unlock(); - notify.notify_one(); thread->join(); } @@ -133,7 +136,6 @@ void DivWorkPool::wait() { if (!threaded) return; if (busyCount==0) { - logV("nothing to do"); return; } @@ -141,12 +143,21 @@ void DivWorkPool::wait() { // start running for (unsigned int i=0; i #include #include -#include #include #include "fixedQueue.h" @@ -45,12 +44,12 @@ struct DivPendingTask { struct DivWorkThread { DivWorkPool* parent; std::mutex lock; - std::mutex selfLock; std::thread* thread; - std::condition_variable notify; + std::promise notify; FixedQueue tasks; std::atomic isBusy; bool terminate; + bool promiseAlreadySet; void run(); bool assign(const std::function& what, void* arg); @@ -62,7 +61,8 @@ struct DivWorkThread { DivWorkThread(): parent(NULL), isBusy(false), - terminate(false) {} + terminate(false), + promiseAlreadySet(false) {} }; /**