From b3b54d5fbd1a9d8827f616bf1b8cdf0b59501a71 Mon Sep 17 00:00:00 2001 From: narzoul Date: Wed, 28 Apr 2021 23:43:12 +0200 Subject: [PATCH] Added ThreadPriorityBoost setting --- DDrawCompat/Config/Config.cpp | 6 ++ DDrawCompat/Config/Config.h | 6 +- DDrawCompat/Config/EnumSetting.cpp | 35 ++++++++ DDrawCompat/Config/EnumSetting.h | 26 ++++++ DDrawCompat/Config/Parser.cpp | 80 +++++++++++++++++-- DDrawCompat/Config/Parser.h | 3 + DDrawCompat/Config/Setting.cpp | 30 +++++++ DDrawCompat/Config/Setting.h | 32 ++++++++ .../Config/Settings/ThreadPriorityBoost.h | 20 +++++ DDrawCompat/DDrawCompat.vcxproj | 10 ++- DDrawCompat/DDrawCompat.vcxproj.filters | 33 ++++++-- DDrawCompat/Dll/DllMain.cpp | 19 +++-- DDrawCompat/Win32/Thread.cpp | 60 ++++++++++++++ .../Win32/{WaitFunctions.h => Thread.h} | 3 +- DDrawCompat/Win32/WaitFunctions.cpp | 52 ------------ 15 files changed, 336 insertions(+), 79 deletions(-) create mode 100644 DDrawCompat/Config/Config.cpp create mode 100644 DDrawCompat/Config/EnumSetting.cpp create mode 100644 DDrawCompat/Config/EnumSetting.h create mode 100644 DDrawCompat/Config/Setting.cpp create mode 100644 DDrawCompat/Config/Setting.h create mode 100644 DDrawCompat/Config/Settings/ThreadPriorityBoost.h create mode 100644 DDrawCompat/Win32/Thread.cpp rename DDrawCompat/Win32/{WaitFunctions.h => Thread.h} (61%) delete mode 100644 DDrawCompat/Win32/WaitFunctions.cpp diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp new file mode 100644 index 0000000..933a18d --- /dev/null +++ b/DDrawCompat/Config/Config.cpp @@ -0,0 +1,6 @@ +#include + +namespace Config +{ + Settings::ThreadPriorityBoost threadPriorityBoost; +} diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 01d3cb2..4beac43 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -1,10 +1,12 @@ #pragma once +#include + namespace Config { const unsigned delayedFlipModeTimeout = 200; const unsigned evictionTimeout = 200; const unsigned maxPaletteUpdatesPerMs = 5; - const unsigned maxUserModeDisplayDrivers = 3; - const unsigned threadSwitchCycleTime = 3 * 1000 * 1000; + + extern Settings::ThreadPriorityBoost threadPriorityBoost; } diff --git a/DDrawCompat/Config/EnumSetting.cpp b/DDrawCompat/Config/EnumSetting.cpp new file mode 100644 index 0000000..92ae068 --- /dev/null +++ b/DDrawCompat/Config/EnumSetting.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace Config +{ + EnumSetting::EnumSetting(const std::string& name, unsigned default, const std::vector& enumNames) + : Setting(name) + , m_default(default) + , m_value(default) + , m_enumNames(enumNames) + { + } + + std::string EnumSetting::getValueStr() const + { + return m_enumNames[m_value]; + } + + void EnumSetting::resetValue() + { + m_value = m_default; + } + + void EnumSetting::setValue(const std::string& value) + { + auto it = std::find(m_enumNames.begin(), m_enumNames.end(), value); + if (it == m_enumNames.end()) + { + throw ParsingError("invalid value: '" + value + "'"); + } + m_value = it - m_enumNames.begin(); + } +} diff --git a/DDrawCompat/Config/EnumSetting.h b/DDrawCompat/Config/EnumSetting.h new file mode 100644 index 0000000..2675dc4 --- /dev/null +++ b/DDrawCompat/Config/EnumSetting.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +namespace Config +{ + class EnumSetting : public Setting + { + public: + unsigned get() const { return m_value; } + + protected: + EnumSetting(const std::string& name, unsigned default, const std::vector& enumNames); + + private: + std::string getValueStr() const override; + void resetValue() override; + void setValue(const std::string& value) override; + + unsigned m_default; + unsigned m_value; + const std::vector m_enumNames; + }; +} diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index b1136c6..5d229e8 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -1,17 +1,27 @@ #include +#include +#include #include #include #include +#include namespace { - void setConfig(const std::string& name, const std::string& value); + void setConfig(const std::string& name, const std::string& value, const std::string& source); + std::string tolower(const std::string& str); std::string trim(const std::string& str); - void loadConfigFile(const std::string& type, const std::filesystem::path& path) + auto& getSettings() { - Compat::Log() << "Loading " << type << " config file: " << path.u8string(); + static std::map settings; + return settings; + } + + void loadConfigFile(const std::string& source, const std::filesystem::path& path) + { + Compat::Log() << "Loading " << source << " config file: " << path.u8string(); std::ifstream f(path); if (!f.is_open()) { @@ -42,7 +52,7 @@ namespace throw Config::ParsingError("missing '=' separator"); } - setConfig(trim(line.substr(0, pos)), trim(line.substr(pos + 1))); + setConfig(trim(line.substr(0, pos)), trim(line.substr(pos + 1)), source); } catch (const Config::ParsingError& error) { @@ -51,14 +61,38 @@ namespace } } - void setConfig(const std::string& name, const std::string& /*value*/) + void setConfig(const std::string& name, const std::string& value, const std::string& source) { if (name.empty()) { throw Config::ParsingError("missing setting name before '='"); } - throw Config::ParsingError("unknown setting: '" + name + "'"); + auto it = std::find_if(getSettings().cbegin(), getSettings().cend(), [&](const auto& v) { + return 0 == _stricmp(v.first.c_str(), name.c_str()); }); + if (it == getSettings().end()) + { + throw Config::ParsingError("unknown setting: '" + name + "'"); + } + + try + { + it->second.set(tolower(value), source); + } + catch (const Config::ParsingError& error) + { + throw Config::ParsingError(it->second.getName() + ": " + error.what()); + } + } + + std::string tolower(const std::string& str) + { + std::string result(str); + for (auto& c : result) + { + c = std::tolower(c, std::locale()); + } + return result; } std::string trim(const std::string& str) @@ -85,6 +119,10 @@ namespace Config { void loadAllConfigFiles(const std::filesystem::path& processPath) { + for (auto& setting : getSettings()) { + setting.second.reset(); + } + loadConfigFile("global", Compat::getEnvPath("PROGRAMDATA") / "DDrawCompat" / "DDrawCompat.ini"); loadConfigFile("user", Compat::getEnvPath("LOCALAPPDATA") / "DDrawCompat" / "DDrawCompat.ini"); loadConfigFile("directory", Compat::replaceFilename(processPath, "DDrawCompat.ini")); @@ -96,6 +134,36 @@ namespace Config } processConfigPath.replace_filename(L"DDrawCompat-" + processConfigPath.filename().native() + L".ini"); loadConfigFile("process", processConfigPath); + + std::size_t maxNameLength = 0; + std::size_t maxSourceLength = 0; + for (const auto& setting : getSettings()) + { + if (setting.second.getName().length() > maxNameLength) + { + maxNameLength = setting.second.getName().length(); + } + if (setting.second.getSource().length() > maxSourceLength) + { + maxSourceLength = setting.second.getSource().length(); + } + } + + Compat::Log() << "Final configuration:"; + for (const auto& setting : getSettings()) + { + std::string name(setting.second.getName()); + name.insert(name.end(), maxNameLength - name.length(), ' '); + std::string source(setting.second.getSource()); + source.insert(source.end(), maxSourceLength - source.length(), ' '); + Compat::Log() << " [" << source << "] " << name << " = " << setting.second.getValueAsString(); + } + } + + void registerSetting(Setting& setting) + { + const auto& name = setting.getName(); + getSettings().emplace(name, setting); } } } diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index 862cdd0..e11ec23 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -11,8 +11,11 @@ namespace Config ParsingError(const std::string& error) : runtime_error(error) {} }; + class Setting; + namespace Parser { void loadAllConfigFiles(const std::filesystem::path& processPath); + void registerSetting(Setting& setting); } } diff --git a/DDrawCompat/Config/Setting.cpp b/DDrawCompat/Config/Setting.cpp new file mode 100644 index 0000000..4ac3ee4 --- /dev/null +++ b/DDrawCompat/Config/Setting.cpp @@ -0,0 +1,30 @@ +#include +#include + +namespace Config +{ + Setting::Setting(const std::string& name) + : m_name(name) + { + Parser::registerSetting(*this); + } + + void Setting::reset() + { + resetValue(); + m_source = "default"; + } + + void Setting::set(const std::string& value, const std::string& source) + { + if ("default" == value) + { + resetValue(); + } + else + { + setValue(value); + } + m_source = source; + } +} diff --git a/DDrawCompat/Config/Setting.h b/DDrawCompat/Config/Setting.h new file mode 100644 index 0000000..a5c369b --- /dev/null +++ b/DDrawCompat/Config/Setting.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace Config +{ + class Setting + { + public: + Setting(const std::string& name); + + Setting(const Setting&) = delete; + Setting(Setting&&) = delete; + Setting& operator=(const Setting&) = delete; + Setting& operator=(Setting&&) = delete; + + const std::string& getName() const { return m_name; } + const std::string& getSource() const { return m_source; } + std::string getValueAsString() const { return getValueStr(); } + + void reset(); + void set(const std::string& value, const std::string& source); + + private: + virtual std::string getValueStr() const = 0; + virtual void resetValue() = 0; + virtual void setValue(const std::string& value) = 0; + + std::string m_name; + std::string m_source; + }; +} diff --git a/DDrawCompat/Config/Settings/ThreadPriorityBoost.h b/DDrawCompat/Config/Settings/ThreadPriorityBoost.h new file mode 100644 index 0000000..a19e6ff --- /dev/null +++ b/DDrawCompat/Config/Settings/ThreadPriorityBoost.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Config +{ + namespace Settings + { + class ThreadPriorityBoost : public EnumSetting + { + public: + enum Value { OFF, ON, MAIN, APP }; + + ThreadPriorityBoost() + : EnumSetting("ThreadPriorityBoost", OFF, { "off", "on", "main", "app" }) + { + } + }; + } +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 1461004..5bc1c0e 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -178,7 +178,10 @@ + + + @@ -265,14 +268,17 @@ - + + + + @@ -341,7 +347,7 @@ - + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index a047521..3c507b0 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -82,6 +82,9 @@ {9d313de7-b8ca-49d0-84d2-217f2818d9d3} + + {bdde0d15-27ab-43a7-9c0a-294df6a52bad} + @@ -327,9 +330,6 @@ Header Files\DDraw - - Header Files\Win32 - Header Files\Direct3d @@ -396,6 +396,18 @@ Header Files\Config + + Header Files\Config + + + Header Files\Config + + + Header Files\Config\Settings + + + Header Files\Win32 + @@ -563,9 +575,6 @@ Source Files\DDraw - - Source Files\Win32 - Source Files\Direct3d @@ -620,6 +629,18 @@ Source Files\Config + + Source Files\Config + + + Source Files\Config + + + Source Files\Config + + + Source Files\Win32 + diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index ab61904..a07a669 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,7 @@ #include #include #include -#include +#include HRESULT WINAPI SetAppCompatData(DWORD, DWORD); @@ -60,8 +61,6 @@ namespace Win32::Registry::installHooks(); Compat::Log() << "Installing Direct3D driver hooks"; D3dDdi::installHooks(); - Compat::Log() << "Installing Win32 hooks"; - Win32::WaitFunctions::installHooks(); Gdi::VirtualScreen::init(); CompatPtr dd; @@ -206,19 +205,19 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) Dll::g_jmpTargetProcs = Dll::g_origProcs; - VISIT_PUBLIC_DDRAW_PROCS(HOOK_DDRAW_PROC) + VISIT_PUBLIC_DDRAW_PROCS(HOOK_DDRAW_PROC); + + Win32::MemoryManagement::installHooks(); + Win32::MsgHooks::installHooks(); + Win32::Thread::installHooks(); + Compat::closeDbgEng(); - const BOOL disablePriorityBoost = TRUE; - SetProcessPriorityBoost(GetCurrentProcess(), disablePriorityBoost); SetProcessAffinityMask(GetCurrentProcess(), 1); timeBeginPeriod(1); setDpiAwareness(); SetThemeAppProperties(0); - - Win32::MemoryManagement::installHooks(); - Win32::MsgHooks::installHooks(); Time::init(); - Compat::closeDbgEng(); + Win32::Thread::applyConfig(); const DWORD disableMaxWindowedMode = 12; CALL_ORIG_PROC(SetAppCompatData)(disableMaxWindowedMode, 0); diff --git a/DDrawCompat/Win32/Thread.cpp b/DDrawCompat/Win32/Thread.cpp new file mode 100644 index 0000000..9409925 --- /dev/null +++ b/DDrawCompat/Win32/Thread.cpp @@ -0,0 +1,60 @@ +#include + +#include +#include +#include +#include + +namespace +{ + BOOL WINAPI setProcessPriorityBoost(HANDLE hProcess, BOOL bDisablePriorityBoost) + { + LOG_FUNC("SetProcessPriorityBoost", hProcess, bDisablePriorityBoost); + if (Config::Settings::ThreadPriorityBoost::APP != Config::threadPriorityBoost.get()) + { + return LOG_RESULT(TRUE); + } + return LOG_RESULT(CALL_ORIG_FUNC(SetProcessPriorityBoost)(hProcess, bDisablePriorityBoost)); + } + + BOOL WINAPI setThreadPriorityBoost(HANDLE hThread, BOOL bDisablePriorityBoost) + { + LOG_FUNC("SetThreadPriorityBoost", hThread, bDisablePriorityBoost); + if (Config::Settings::ThreadPriorityBoost::APP != Config::threadPriorityBoost.get()) + { + return LOG_RESULT(TRUE); + } + return LOG_RESULT(CALL_ORIG_FUNC(SetThreadPriorityBoost)(hThread, bDisablePriorityBoost)); + } +} + +namespace Win32 +{ + namespace Thread + { + void applyConfig() + { + switch (Config::threadPriorityBoost.get()) + { + case Config::Settings::ThreadPriorityBoost::OFF: + CALL_ORIG_FUNC(SetProcessPriorityBoost)(GetCurrentProcess(), TRUE); + break; + + case Config::Settings::ThreadPriorityBoost::ON: + CALL_ORIG_FUNC(SetProcessPriorityBoost)(GetCurrentProcess(), FALSE); + break; + + case Config::Settings::ThreadPriorityBoost::MAIN: + CALL_ORIG_FUNC(SetProcessPriorityBoost)(GetCurrentProcess(), TRUE); + CALL_ORIG_FUNC(SetThreadPriorityBoost)(GetCurrentThread(), FALSE); + break; + } + } + + void installHooks() + { + HOOK_FUNCTION(kernel32, SetProcessPriorityBoost, setProcessPriorityBoost); + HOOK_FUNCTION(kernel32, SetThreadPriorityBoost, setThreadPriorityBoost); + } + } +} diff --git a/DDrawCompat/Win32/WaitFunctions.h b/DDrawCompat/Win32/Thread.h similarity index 61% rename from DDrawCompat/Win32/WaitFunctions.h rename to DDrawCompat/Win32/Thread.h index 0b095d6..e0ad174 100644 --- a/DDrawCompat/Win32/WaitFunctions.h +++ b/DDrawCompat/Win32/Thread.h @@ -2,8 +2,9 @@ namespace Win32 { - namespace WaitFunctions + namespace Thread { + void applyConfig(); void installHooks(); } } diff --git a/DDrawCompat/Win32/WaitFunctions.cpp b/DDrawCompat/Win32/WaitFunctions.cpp deleted file mode 100644 index 9d29472..0000000 --- a/DDrawCompat/Win32/WaitFunctions.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -namespace -{ - void mitigateBusyWaiting() - { - thread_local ULONG64 ctLastThreadSwitch = Time::queryThreadCycleTime(); - ULONG64 ctNow = Time::queryThreadCycleTime(); - if (ctNow - ctLastThreadSwitch >= Config::threadSwitchCycleTime) - { - Sleep(0); - ctLastThreadSwitch = ctNow; - } - } - - template - Result WINAPI mitigatedBusyWaitingFunc(Params... params) - { - mitigateBusyWaiting(); - return Compat::g_origFuncPtr(params...); - } -} - -#define MITIGATE_BUSY_WAITING(module, func) \ - Compat::hookFunction<&func>(#module, #func, &mitigatedBusyWaitingFunc<&func>) - -namespace Win32 -{ - namespace WaitFunctions - { - void installHooks() - { - MITIGATE_BUSY_WAITING(user32, GetMessageA); - MITIGATE_BUSY_WAITING(user32, GetMessageW); - MITIGATE_BUSY_WAITING(kernel32, GetTickCount); - MITIGATE_BUSY_WAITING(user32, MsgWaitForMultipleObjects); - MITIGATE_BUSY_WAITING(user32, MsgWaitForMultipleObjectsEx); - MITIGATE_BUSY_WAITING(user32, PeekMessageA); - MITIGATE_BUSY_WAITING(user32, PeekMessageW); - MITIGATE_BUSY_WAITING(kernel32, SignalObjectAndWait); - MITIGATE_BUSY_WAITING(winmm, timeGetTime); - MITIGATE_BUSY_WAITING(kernel32, WaitForSingleObjectEx); - MITIGATE_BUSY_WAITING(kernel32, WaitForMultipleObjectsEx); - } - } -}