diff --git a/src/CMakeData-os.cmake b/src/CMakeData-os.cmake index abe3752af1..9434ff1ec8 100644 --- a/src/CMakeData-os.cmake +++ b/src/CMakeData-os.cmake @@ -57,6 +57,7 @@ else() "archutils/Win32/RegistryAccess.cpp" "archutils/Win32/RestartProgram.cpp" "archutils/Win32/SpecialDirs.cpp" + "archutils/Win32/ThreadPriorityHelper.cpp" "archutils/Win32/USB.cpp" "archutils/Win32/VideoDriverInfo.cpp" "archutils/Win32/WindowIcon.cpp" @@ -82,6 +83,7 @@ else() "archutils/Win32/RegistryAccess.h" "archutils/Win32/RestartProgram.h" "archutils/Win32/SpecialDirs.h" + "archutils/Win32/ThreadPriorityHelper.h" "archutils/Win32/USB.h" "archutils/Win32/VideoDriverInfo.h" "archutils/Win32/WindowIcon.h" diff --git a/src/GameLoop.cpp b/src/GameLoop.cpp index 2eb7262c3e..6e6475c119 100644 --- a/src/GameLoop.cpp +++ b/src/GameLoop.cpp @@ -24,6 +24,9 @@ #include #include +#ifdef _WIN32 +#include "archutils/Win32/ThreadPriorityHelper.h" +#endif static RageTimer g_GameplayTimer; @@ -410,6 +413,13 @@ void ConcurrentRenderer::RenderThread() if( m_State == RENDERING_START ) { +#ifdef _WIN32 + bool setThreadSuccess = BoostThreadPriorityToHighest(); + if (!setThreadSuccess) + { + ASSERT_M(0, "Failed to set thread priority to highest."); + } +#endif /* We're starting to render. Set up, and then kick the event to wake * up the calling thread. */ DISPLAY->BeginConcurrentRendering(); diff --git a/src/RageSoundReader_ThreadedBuffer.h b/src/RageSoundReader_ThreadedBuffer.h index bcf0c6b371..af69262a72 100644 --- a/src/RageSoundReader_ThreadedBuffer.h +++ b/src/RageSoundReader_ThreadedBuffer.h @@ -8,6 +8,10 @@ #include "RageThreads.h" #include +#ifdef _WIN32 +#include "archutils/Win32/ThreadPriorityHelper.h" +#endif + class RageThread; class RageSoundReader_ThreadedBuffer: public RageSoundReader_Filter { @@ -70,7 +74,19 @@ private: RageThread m_Thread; bool m_bShutdownThread; - static int StartBufferingThread( void *p ) { ((RageSoundReader_ThreadedBuffer *) p)->BufferingThread(); return 0; } + static int StartBufferingThread(void* p) + { +#ifdef _WIN32 + bool setThreadSuccess = BoostThreadPriorityToHighest(); + if (!setThreadSuccess) + { + ASSERT_M(0, "Failed to set thread priority to highest."); + } +#endif + ((RageSoundReader_ThreadedBuffer*)p)->BufferingThread(); + return 0; + } + void BufferingThread(); }; diff --git a/src/archutils/Win32/ThreadPriorityHelper.cpp b/src/archutils/Win32/ThreadPriorityHelper.cpp new file mode 100644 index 0000000000..d66ec9bdf3 --- /dev/null +++ b/src/archutils/Win32/ThreadPriorityHelper.cpp @@ -0,0 +1,33 @@ +#include "ThreadPriorityHelper.h" +#define WIN32_LEAN_AND_MEAN +#include + +bool SetThreadPriorityLevel(int priorityLevel) +{ + return SetThreadPriority(GetCurrentThread(), priorityLevel) != 0; +} + +bool BoostThreadPriorityToBelowNormal() +{ + return SetThreadPriorityLevel(THREAD_PRIORITY_BELOW_NORMAL); +} + +bool BoostThreadPriorityToNormal() +{ + return SetThreadPriorityLevel(THREAD_PRIORITY_NORMAL); +} + +bool BoostThreadPriorityToAboveNormal() +{ + return SetThreadPriorityLevel(THREAD_PRIORITY_ABOVE_NORMAL); +} + +bool BoostThreadPriorityToHighest() +{ + return SetThreadPriorityLevel(THREAD_PRIORITY_HIGHEST); +} + +bool BoostThreadPriorityToTimeCritical() +{ + return SetThreadPriorityLevel(THREAD_PRIORITY_TIME_CRITICAL); +} diff --git a/src/archutils/Win32/ThreadPriorityHelper.h b/src/archutils/Win32/ThreadPriorityHelper.h new file mode 100644 index 0000000000..5adaeec4ed --- /dev/null +++ b/src/archutils/Win32/ThreadPriorityHelper.h @@ -0,0 +1,70 @@ +/*++ + +A layer to isolate windows.h from other source files while retaining the ability to set thread priorities. + + One possible way to call it if you don't need to store the result is like so: + + #ifdef _WIN32 + bool SuccessStatus = BoostThreadPriorityToHighest(); + if (!SuccessStatus) + { + ASSERT_M(0, "Failed to set thread priority to highest."); + } + #endif + +Please refer to the Windows SetThreadPriority function documentation for more information. +https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority + +Please maintain this priority structure for the threads: + +Highest or Time Critical +* Rendering thread +* Audio mixing thread + - If this is not high enough priority, it can negatively affect the game clock + +Above Normal or Highest +* Audio decoding thread +* Audio streaming thread +* Audio buffering thread + +Normal +* Main game thread + - Please keep this below the audio thread priority + +Below Normal +* Worker threads + +--*/ + +bool SetThreadPriorityLevel(int priorityLevel); + +bool BoostThreadPriorityToBelowNormal(); +bool BoostThreadPriorityToNormal(); +bool BoostThreadPriorityToAboveNormal(); +bool BoostThreadPriorityToHighest(); +bool BoostThreadPriorityToTimeCritical(); + +/** + * (c) sukibaby 2024 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */