Cocoa Port: Don't set thread priorities on single-core systems, hopefully preventing thread starvation and helping single-core systems run more stable.

This commit is contained in:
rogerman
2025-08-07 21:20:17 -07:00
parent 4ca5101f52
commit a7812dcea5
6 changed files with 149 additions and 75 deletions

View File

@@ -162,6 +162,7 @@ private:
pthread_mutex_t _mutexApplyGPUSettings; pthread_mutex_t _mutexApplyGPUSettings;
pthread_mutex_t _mutexApplyRender3DSettings; pthread_mutex_t _mutexApplyRender3DSettings;
bool _render3DNeedsFinish; bool _render3DNeedsFinish;
int _cpuCoreCountRestoreValue;
public: public:
GPUEventHandlerAsync(); GPUEventHandlerAsync();
@@ -181,6 +182,8 @@ public:
bool GetRender3DNeedsFinish(); bool GetRender3DNeedsFinish();
void SetTempThreadCount(int threadCount);
#ifdef ENABLE_ASYNC_FETCH #ifdef ENABLE_ASYNC_FETCH
virtual void DidFrameBegin(const size_t line, const bool isFrameSkipRequested, const size_t pageCount, u8 &selectedBufferIndexInOut); virtual void DidFrameBegin(const size_t line, const bool isFrameSkipRequested, const size_t pageCount, u8 &selectedBufferIndexInOut);
virtual void DidFrameEnd(bool isFrameSkipped, const NDSDisplayInfo &latestDisplayInfo); virtual void DidFrameEnd(bool isFrameSkipped, const NDSDisplayInfo &latestDisplayInfo);
@@ -209,6 +212,8 @@ public:
NSUInteger openglDeviceMaxMultisamples; NSUInteger openglDeviceMaxMultisamples;
NSString *render3DMultisampleSizeString; NSString *render3DMultisampleSizeString;
BOOL isCPUCoreCountAuto; BOOL isCPUCoreCountAuto;
int _render3DThreadsRequested;
int _render3DThreadCount;
BOOL _needRestoreRender3DLock; BOOL _needRestoreRender3DLock;
apple_unfairlock_t _unfairlockGpuState; apple_unfairlock_t _unfairlockGpuState;

View File

@@ -127,6 +127,8 @@ char __hostRendererString[256] = {0};
GPUSTATE_SUB_OBJ_MASK; GPUSTATE_SUB_OBJ_MASK;
isCPUCoreCountAuto = NO; isCPUCoreCountAuto = NO;
_render3DThreadsRequested = 0;
_render3DThreadCount = 0;
_needRestoreRender3DLock = NO; _needRestoreRender3DLock = NO;
oglrender_init = &cgl_initOpenGL_StandardAuto; oglrender_init = &cgl_initOpenGL_StandardAuto;
@@ -394,6 +396,13 @@ char __hostRendererString[256] = {0};
gpuEvent->ApplyRender3DSettingsLock(); gpuEvent->ApplyRender3DSettingsLock();
GPU->Set3DRendererByID((int)rendererID); GPU->Set3DRendererByID((int)rendererID);
if (rendererID == CORE3DLIST_SWRASTERIZE)
{
gpuEvent->SetTempThreadCount(_render3DThreadCount);
GPU->Set3DRendererByID(CORE3DLIST_SWRASTERIZE);
}
gpuEvent->ApplyRender3DSettingsUnlock(); gpuEvent->ApplyRender3DSettingsUnlock();
} }
@@ -554,34 +563,38 @@ char __hostRendererString[256] = {0};
- (void) setRender3DThreads:(NSUInteger)numberThreads - (void) setRender3DThreads:(NSUInteger)numberThreads
{ {
NSUInteger numberCores = [[NSProcessInfo processInfo] activeProcessorCount]; _render3DThreadsRequested = (int)numberThreads;
const int numberCores = CommonSettings.num_cores;
int newThreadCount = numberCores;
if (numberThreads == 0) if (numberThreads == 0)
{ {
isCPUCoreCountAuto = YES; isCPUCoreCountAuto = YES;
if (numberCores < 2) if (numberCores < 2)
{ {
numberCores = 1; newThreadCount = 1;
} }
else else
{ {
const NSUInteger reserveCoreCount = numberCores / 12; // For every 12 cores, reserve 1 core for the rest of the system. const int reserveCoreCount = numberCores / 12; // For every 12 cores, reserve 1 core for the rest of the system.
numberCores -= reserveCoreCount; newThreadCount -= reserveCoreCount;
} }
} }
else else
{ {
isCPUCoreCountAuto = NO; isCPUCoreCountAuto = NO;
numberCores = numberThreads; newThreadCount = (int)numberThreads;
} }
const RendererID renderingEngineID = (RendererID)[self render3DRenderingEngine]; const RendererID renderingEngineID = (RendererID)[self render3DRenderingEngine];
_render3DThreadCount = newThreadCount;
gpuEvent->ApplyRender3DSettingsLock(); gpuEvent->ApplyRender3DSettingsLock();
CommonSettings.num_cores = (int)numberCores;
if (renderingEngineID == RENDERID_SOFTRASTERIZER) if (renderingEngineID == RENDERID_SOFTRASTERIZER)
{ {
gpuEvent->SetTempThreadCount(newThreadCount);
GPU->Set3DRendererByID(renderingEngineID); GPU->Set3DRendererByID(renderingEngineID);
} }
@@ -590,11 +603,7 @@ char __hostRendererString[256] = {0};
- (NSUInteger) render3DThreads - (NSUInteger) render3DThreads
{ {
gpuEvent->ApplyRender3DSettingsLock(); return (isCPUCoreCountAuto) ? 0 : (NSUInteger)_render3DThreadsRequested;
const NSUInteger numberThreads = isCPUCoreCountAuto ? 0 : (NSUInteger)CommonSettings.num_cores;
gpuEvent->ApplyRender3DSettingsUnlock();
return numberThreads;
} }
- (void) setRender3DLineHack:(BOOL)state - (void) setRender3DLineHack:(BOOL)state
@@ -1240,17 +1249,24 @@ MacGPUFetchObjectAsync::~MacGPUFetchObjectAsync()
void MacGPUFetchObjectAsync::Init() void MacGPUFetchObjectAsync::Init()
{ {
pthread_attr_t threadAttr; if (CommonSettings.num_cores > 1)
pthread_attr_init(&threadAttr); {
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR); pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
struct sched_param sp; pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 44; struct sched_param sp;
pthread_attr_setschedparam(&threadAttr, &sp); memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 44;
pthread_create(&_threadFetch, &threadAttr, &RunFetchThread, this); pthread_attr_setschedparam(&threadAttr, &sp);
pthread_attr_destroy(&threadAttr);
pthread_create(&_threadFetch, &threadAttr, &RunFetchThread, this);
pthread_attr_destroy(&threadAttr);
}
else
{
pthread_create(&_threadFetch, NULL, &RunFetchThread, this);
}
} }
void MacGPUFetchObjectAsync::SemaphoreFramebufferCreate() void MacGPUFetchObjectAsync::SemaphoreFramebufferCreate()
@@ -1744,6 +1760,8 @@ GPUEventHandlerAsync::GPUEventHandlerAsync()
{ {
_fetchObject = nil; _fetchObject = nil;
_render3DNeedsFinish = false; _render3DNeedsFinish = false;
_cpuCoreCountRestoreValue = 0;
pthread_mutex_init(&_mutexFrame, NULL); pthread_mutex_init(&_mutexFrame, NULL);
pthread_mutex_init(&_mutex3DRender, NULL); pthread_mutex_init(&_mutex3DRender, NULL);
pthread_mutex_init(&_mutexApplyGPUSettings, NULL); pthread_mutex_init(&_mutexApplyGPUSettings, NULL);
@@ -1843,6 +1861,12 @@ void GPUEventHandlerAsync::DidApplyRender3DSettingsBegin()
void GPUEventHandlerAsync::DidApplyRender3DSettingsEnd() void GPUEventHandlerAsync::DidApplyRender3DSettingsEnd()
{ {
if (this->_cpuCoreCountRestoreValue > 0)
{
CommonSettings.num_cores = this->_cpuCoreCountRestoreValue;
}
this->_cpuCoreCountRestoreValue = 0;
this->ApplyRender3DSettingsUnlock(); this->ApplyRender3DSettingsUnlock();
} }
@@ -1891,6 +1915,19 @@ bool GPUEventHandlerAsync::GetRender3DNeedsFinish()
return this->_render3DNeedsFinish; return this->_render3DNeedsFinish;
} }
void GPUEventHandlerAsync::SetTempThreadCount(int threadCount)
{
if (threadCount < 1)
{
this->_cpuCoreCountRestoreValue = 0;
}
else
{
this->_cpuCoreCountRestoreValue = CommonSettings.num_cores;
CommonSettings.num_cores = threadCount;
}
}
#pragma mark - #pragma mark -
#if !defined(MAC_OS_X_VERSION_10_7) #if !defined(MAC_OS_X_VERSION_10_7)

View File

@@ -141,30 +141,37 @@ volatile bool execute = true;
pthread_cond_init(&threadParam.condThreadExecute, NULL); pthread_cond_init(&threadParam.condThreadExecute, NULL);
pthread_rwlock_init(&threadParam.rwlockCoreExecute, NULL); pthread_rwlock_init(&threadParam.rwlockCoreExecute, NULL);
// The core emulation thread needs max priority since it is the sole if (CommonSettings.num_cores > 1)
// producer thread for all output threads. Note that this is not being {
// done for performance -- this is being done for timing accuracy. The // The core emulation thread needs max priority since it is the sole
// core emulation thread is responsible for determining the emulator's // producer thread for all output threads. Note that this is not being
// timing. If one output thread interferes with timing, then it ends up // done for performance -- this is being done for timing accuracy. The
// affecting the whole emulator. // core emulation thread is responsible for determining the emulator's
// // timing. If one output thread interferes with timing, then it ends up
// Though it may be tempting to make this a real-time thread, it's best // affecting the whole emulator.
// to keep this a normal thread. The core emulation thread can use up a //
// lot of CPU time under certain conditions, which may interfere with // Though it may be tempting to make this a real-time thread, it's best
// other threads. (Example: Video tearing on display windows, even with // to keep this a normal thread. The core emulation thread can use up a
// V-sync enabled.) // lot of CPU time under certain conditions, which may interfere with
// other threads. (Example: Video tearing on display windows, even with
pthread_attr_t threadAttr; // V-sync enabled.)
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR); pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
struct sched_param sp; pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 42; struct sched_param sp;
pthread_attr_setschedparam(&threadAttr, &sp); memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 42;
pthread_create(&coreThread, &threadAttr, &RunCoreThread, &threadParam); pthread_attr_setschedparam(&threadAttr, &sp);
pthread_attr_destroy(&threadAttr);
pthread_create(&coreThread, &threadAttr, &RunCoreThread, &threadParam);
pthread_attr_destroy(&threadAttr);
}
else
{
pthread_create(&coreThread, NULL, &RunCoreThread, &threadParam);
}
[cdsGPU setOutputList:cdsOutputList rwlock:&threadParam.rwlockOutputList]; [cdsGPU setOutputList:cdsOutputList rwlock:&threadParam.rwlockOutputList];
[cdsCheatManager setRwlockCoreExecute:&threadParam.rwlockCoreExecute]; [cdsCheatManager setRwlockCoreExecute:&threadParam.rwlockCoreExecute];

View File

@@ -99,17 +99,24 @@
pthread_mutex_init(&_mutexMessageLoop, NULL); pthread_mutex_init(&_mutexMessageLoop, NULL);
pthread_cond_init(&_condSignalMessage, NULL); pthread_cond_init(&_condSignalMessage, NULL);
pthread_attr_t threadAttr; if (CommonSettings.num_cores > 1)
pthread_attr_init(&threadAttr); {
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR); pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
struct sched_param sp; pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 45; struct sched_param sp;
pthread_attr_setschedparam(&threadAttr, &sp); memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 45;
pthread_create(&_pthread, &threadAttr, &RunOutputThread, self); pthread_attr_setschedparam(&threadAttr, &sp);
pthread_attr_destroy(&threadAttr);
pthread_create(&_pthread, &threadAttr, &RunOutputThread, self);
pthread_attr_destroy(&threadAttr);
}
else
{
pthread_create(&_pthread, NULL, &RunOutputThread, self);
}
} }
- (void) exitThread - (void) exitThread

View File

@@ -1832,13 +1832,19 @@ SoftRasterizerRenderer::SoftRasterizerRenderer()
char name[16]; char name[16];
snprintf(name, 16, "rasterizer %d", (int)i); snprintf(name, 16, "rasterizer %d", (int)i);
#ifdef DESMUME_COCOA #ifdef DESMUME_COCOA
// The Cocoa port takes advantage of hand-optimized thread priorities if (coreCount > 1)
// to help stabilize performance when running SoftRasterizer. {
_task[i].start(false, 43, name); // The Cocoa port takes advantage of hand-optimized thread priorities
#else // to help stabilize performance when running SoftRasterizer.
_task[i].start(false, 0, name); _task[i].start(false, 43, name);
}
else
#endif #endif
{
_task[i].start(false, 0, name);
}
} }
} }

View File

@@ -3395,12 +3395,18 @@ bool AdhocCommInterface::Start(WifiHandler* currentWifiHandler)
// Start the RX packet thread. // Start the RX packet thread.
this->_wifiHandler = currentWifiHandler; this->_wifiHandler = currentWifiHandler;
this->_rawPacket = (RXRawPacketData*)calloc(1, sizeof(RXRawPacketData)); this->_rawPacket = (RXRawPacketData*)calloc(1, sizeof(RXRawPacketData));
#ifdef DESMUME_COCOA #ifdef DESMUME_COCOA
this->_rxTask->start(false, 43, "wifi ad-hoc"); if (CommonSettings.num_cores > 1)
#else {
this->_rxTask->start(false, 0, "wifi ad-hoc"); this->_rxTask->start(false, 43, "wifi ad-hoc");
#endif }
else
#endif
{
this->_rxTask->start(false, 0, "wifi ad-hoc");
}
this->_isRXThreadRunning = true; this->_isRXThreadRunning = true;
this->_rxTask->execute(&Adhoc_RXPacketGetOnThread, this); this->_rxTask->execute(&Adhoc_RXPacketGetOnThread, this);
@@ -3669,12 +3675,18 @@ bool SoftAPCommInterface::Start(WifiHandler* currentWifiHandler)
// Start the RX packet thread. // Start the RX packet thread.
this->_wifiHandler = currentWifiHandler; this->_wifiHandler = currentWifiHandler;
this->_rawPacket = (RXRawPacketData*)calloc(1, sizeof(RXRawPacketData)); this->_rawPacket = (RXRawPacketData*)calloc(1, sizeof(RXRawPacketData));
#ifdef DESMUME_COCOA #ifdef DESMUME_COCOA
this->_rxTask->start(false, 43, "wifi ap"); if (CommonSettings.num_cores > 1)
#else {
this->_rxTask->start(false, 0, "wifi ap"); this->_rxTask->start(false, 43, "wifi ap");
#endif }
else
#endif
{
this->_rxTask->start(false, 0, "wifi ap");
}
this->_isRXThreadRunning = true; this->_isRXThreadRunning = true;
this->_rxTask->execute(&Infrastructure_RXPacketGetOnThread, this); this->_rxTask->execute(&Infrastructure_RXPacketGetOnThread, this);
} }