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

View File

@@ -127,6 +127,8 @@ char __hostRendererString[256] = {0};
GPUSTATE_SUB_OBJ_MASK;
isCPUCoreCountAuto = NO;
_render3DThreadsRequested = 0;
_render3DThreadCount = 0;
_needRestoreRender3DLock = NO;
oglrender_init = &cgl_initOpenGL_StandardAuto;
@@ -394,6 +396,13 @@ char __hostRendererString[256] = {0};
gpuEvent->ApplyRender3DSettingsLock();
GPU->Set3DRendererByID((int)rendererID);
if (rendererID == CORE3DLIST_SWRASTERIZE)
{
gpuEvent->SetTempThreadCount(_render3DThreadCount);
GPU->Set3DRendererByID(CORE3DLIST_SWRASTERIZE);
}
gpuEvent->ApplyRender3DSettingsUnlock();
}
@@ -554,34 +563,38 @@ char __hostRendererString[256] = {0};
- (void) setRender3DThreads:(NSUInteger)numberThreads
{
NSUInteger numberCores = [[NSProcessInfo processInfo] activeProcessorCount];
_render3DThreadsRequested = (int)numberThreads;
const int numberCores = CommonSettings.num_cores;
int newThreadCount = numberCores;
if (numberThreads == 0)
{
isCPUCoreCountAuto = YES;
if (numberCores < 2)
{
numberCores = 1;
newThreadCount = 1;
}
else
{
const NSUInteger reserveCoreCount = numberCores / 12; // For every 12 cores, reserve 1 core for the rest of the system.
numberCores -= reserveCoreCount;
const int reserveCoreCount = numberCores / 12; // For every 12 cores, reserve 1 core for the rest of the system.
newThreadCount -= reserveCoreCount;
}
}
else
{
isCPUCoreCountAuto = NO;
numberCores = numberThreads;
newThreadCount = (int)numberThreads;
}
const RendererID renderingEngineID = (RendererID)[self render3DRenderingEngine];
_render3DThreadCount = newThreadCount;
gpuEvent->ApplyRender3DSettingsLock();
CommonSettings.num_cores = (int)numberCores;
if (renderingEngineID == RENDERID_SOFTRASTERIZER)
{
gpuEvent->SetTempThreadCount(newThreadCount);
GPU->Set3DRendererByID(renderingEngineID);
}
@@ -590,11 +603,7 @@ char __hostRendererString[256] = {0};
- (NSUInteger) render3DThreads
{
gpuEvent->ApplyRender3DSettingsLock();
const NSUInteger numberThreads = isCPUCoreCountAuto ? 0 : (NSUInteger)CommonSettings.num_cores;
gpuEvent->ApplyRender3DSettingsUnlock();
return numberThreads;
return (isCPUCoreCountAuto) ? 0 : (NSUInteger)_render3DThreadsRequested;
}
- (void) setRender3DLineHack:(BOOL)state
@@ -1240,17 +1249,24 @@ MacGPUFetchObjectAsync::~MacGPUFetchObjectAsync()
void MacGPUFetchObjectAsync::Init()
{
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
struct sched_param sp;
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 44;
pthread_attr_setschedparam(&threadAttr, &sp);
pthread_create(&_threadFetch, &threadAttr, &RunFetchThread, this);
pthread_attr_destroy(&threadAttr);
if (CommonSettings.num_cores > 1)
{
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
struct sched_param sp;
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 44;
pthread_attr_setschedparam(&threadAttr, &sp);
pthread_create(&_threadFetch, &threadAttr, &RunFetchThread, this);
pthread_attr_destroy(&threadAttr);
}
else
{
pthread_create(&_threadFetch, NULL, &RunFetchThread, this);
}
}
void MacGPUFetchObjectAsync::SemaphoreFramebufferCreate()
@@ -1744,6 +1760,8 @@ GPUEventHandlerAsync::GPUEventHandlerAsync()
{
_fetchObject = nil;
_render3DNeedsFinish = false;
_cpuCoreCountRestoreValue = 0;
pthread_mutex_init(&_mutexFrame, NULL);
pthread_mutex_init(&_mutex3DRender, NULL);
pthread_mutex_init(&_mutexApplyGPUSettings, NULL);
@@ -1843,6 +1861,12 @@ void GPUEventHandlerAsync::DidApplyRender3DSettingsBegin()
void GPUEventHandlerAsync::DidApplyRender3DSettingsEnd()
{
if (this->_cpuCoreCountRestoreValue > 0)
{
CommonSettings.num_cores = this->_cpuCoreCountRestoreValue;
}
this->_cpuCoreCountRestoreValue = 0;
this->ApplyRender3DSettingsUnlock();
}
@@ -1891,6 +1915,19 @@ bool GPUEventHandlerAsync::GetRender3DNeedsFinish()
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 -
#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_rwlock_init(&threadParam.rwlockCoreExecute, NULL);
// The core emulation thread needs max priority since it is the sole
// producer thread for all output threads. Note that this is not being
// done for performance -- this is being done for timing accuracy. The
// core emulation thread is responsible for determining the emulator's
// timing. If one output thread interferes with timing, then it ends up
// affecting the whole emulator.
//
// Though it may be tempting to make this a real-time thread, it's best
// 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
// other threads. (Example: Video tearing on display windows, even with
// V-sync enabled.)
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
struct sched_param sp;
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 42;
pthread_attr_setschedparam(&threadAttr, &sp);
pthread_create(&coreThread, &threadAttr, &RunCoreThread, &threadParam);
pthread_attr_destroy(&threadAttr);
if (CommonSettings.num_cores > 1)
{
// The core emulation thread needs max priority since it is the sole
// producer thread for all output threads. Note that this is not being
// done for performance -- this is being done for timing accuracy. The
// core emulation thread is responsible for determining the emulator's
// timing. If one output thread interferes with timing, then it ends up
// affecting the whole emulator.
//
// Though it may be tempting to make this a real-time thread, it's best
// 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
// other threads. (Example: Video tearing on display windows, even with
// V-sync enabled.)
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
struct sched_param sp;
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority = 42;
pthread_attr_setschedparam(&threadAttr, &sp);
pthread_create(&coreThread, &threadAttr, &RunCoreThread, &threadParam);
pthread_attr_destroy(&threadAttr);
}
else
{
pthread_create(&coreThread, NULL, &RunCoreThread, &threadParam);
}
[cdsGPU setOutputList:cdsOutputList rwlock:&threadParam.rwlockOutputList];
[cdsCheatManager setRwlockCoreExecute:&threadParam.rwlockCoreExecute];

View File

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

View File

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

View File

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