From 74460b2d24a58439d3d2725c7c431dbe7bc6f8aa Mon Sep 17 00:00:00 2001 From: narzoul Date: Sat, 1 May 2021 01:43:46 +0200 Subject: [PATCH] Added CpuAffinity setting --- DDrawCompat/Config/Config.cpp | 1 + DDrawCompat/Config/Config.h | 2 + DDrawCompat/Config/ListSetting.cpp | 36 ++++++++++ DDrawCompat/Config/ListSetting.h | 21 ++++++ DDrawCompat/Config/Parser.cpp | 46 +++++++------ DDrawCompat/Config/Parser.h | 3 + DDrawCompat/Config/Settings/CpuAffinity.cpp | 73 +++++++++++++++++++++ DDrawCompat/Config/Settings/CpuAffinity.h | 23 +++++++ DDrawCompat/DDrawCompat.vcxproj | 4 ++ DDrawCompat/DDrawCompat.vcxproj.filters | 15 +++++ DDrawCompat/Dll/DllMain.cpp | 1 - DDrawCompat/Win32/Thread.cpp | 27 ++++++++ 12 files changed, 232 insertions(+), 20 deletions(-) create mode 100644 DDrawCompat/Config/ListSetting.cpp create mode 100644 DDrawCompat/Config/ListSetting.h create mode 100644 DDrawCompat/Config/Settings/CpuAffinity.cpp create mode 100644 DDrawCompat/Config/Settings/CpuAffinity.h diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index 933a18d..2035994 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -2,5 +2,6 @@ namespace Config { + Settings::CpuAffinity cpuAffinity; Settings::ThreadPriorityBoost threadPriorityBoost; } diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 4beac43..4768580 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -1,5 +1,6 @@ #pragma once +#include #include namespace Config @@ -8,5 +9,6 @@ namespace Config const unsigned evictionTimeout = 200; const unsigned maxPaletteUpdatesPerMs = 5; + extern Settings::CpuAffinity cpuAffinity; extern Settings::ThreadPriorityBoost threadPriorityBoost; } diff --git a/DDrawCompat/Config/ListSetting.cpp b/DDrawCompat/Config/ListSetting.cpp new file mode 100644 index 0000000..dd7dad1 --- /dev/null +++ b/DDrawCompat/Config/ListSetting.cpp @@ -0,0 +1,36 @@ +#include +#include + +namespace Config +{ + ListSetting::ListSetting(const std::string& name, const std::string& default) + : Setting(name) + , m_default(default) + { + } + + void ListSetting::resetValue() + { + setValue(m_default); + } + + void ListSetting::setValue(const std::string& value) + { + std::vector values; + std::size_t startPos = 0; + std::size_t endPos = 0; + + do + { + endPos = value.find(',', startPos); + if (endPos == std::string::npos) + { + endPos = value.length(); + } + values.push_back(Parser::trim(value.substr(startPos, endPos - startPos))); + startPos = endPos + 1; + } while (endPos < value.length()); + + setValues(values); + } +} diff --git a/DDrawCompat/Config/ListSetting.h b/DDrawCompat/Config/ListSetting.h new file mode 100644 index 0000000..951c990 --- /dev/null +++ b/DDrawCompat/Config/ListSetting.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +namespace Config +{ + class ListSetting : public Setting + { + protected: + ListSetting(const std::string& name, const std::string& default); + + private: + void resetValue() override; + void setValue(const std::string& value) override; + virtual void setValues(const std::vector& values) = 0; + + std::string m_default; + }; +} diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index 5d229e8..1c49851 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -11,7 +11,6 @@ namespace { 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); auto& getSettings() { @@ -52,7 +51,7 @@ namespace throw Config::ParsingError("missing '=' separator"); } - setConfig(trim(line.substr(0, pos)), trim(line.substr(pos + 1)), source); + setConfig(Config::Parser::trim(line.substr(0, pos)), Config::Parser::trim(line.substr(pos + 1)), source); } catch (const Config::ParsingError& error) { @@ -94,23 +93,6 @@ namespace } return result; } - - std::string trim(const std::string& str) - { - auto result(str); - auto pos = str.find_last_not_of(" \t"); - if (pos != std::string::npos) - { - result.resize(pos + 1); - } - - pos = result.find_first_not_of(" \t"); - if (pos != std::string::npos) - { - result = result.substr(pos); - } - return result; - } } namespace Config @@ -160,10 +142,36 @@ namespace Config } } + unsigned parseUnsigned(const std::string& value) + { + if (value.empty() || std::string::npos != value.find_first_not_of("0123456789")) + { + throw ParsingError("not an unsigned integer: '" + value + "'"); + } + return std::strtoul(value.c_str(), nullptr, 10); + } + void registerSetting(Setting& setting) { const auto& name = setting.getName(); getSettings().emplace(name, setting); } + + std::string trim(const std::string& str) + { + auto result(str); + auto pos = str.find_last_not_of(" \t"); + if (pos != std::string::npos) + { + result.resize(pos + 1); + } + + pos = result.find_first_not_of(" \t"); + if (pos != std::string::npos) + { + result = result.substr(pos); + } + return result; + } } } diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index e11ec23..68d880e 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -2,6 +2,7 @@ #include #include +#include namespace Config { @@ -16,6 +17,8 @@ namespace Config namespace Parser { void loadAllConfigFiles(const std::filesystem::path& processPath); + unsigned parseUnsigned(const std::string& value); void registerSetting(Setting& setting); + std::string trim(const std::string& str); } } diff --git a/DDrawCompat/Config/Settings/CpuAffinity.cpp b/DDrawCompat/Config/Settings/CpuAffinity.cpp new file mode 100644 index 0000000..c16ac04 --- /dev/null +++ b/DDrawCompat/Config/Settings/CpuAffinity.cpp @@ -0,0 +1,73 @@ +#include +#include + +namespace Config +{ + namespace Settings + { + CpuAffinity::CpuAffinity() + : ListSetting("CpuAffinity", "1") + , m_value(1) + { + } + + std::string CpuAffinity::getValueStr() const + { + if (0 == m_value) + { + return "app"; + } + + if (UINT_MAX == m_value) + { + return "all"; + } + + std::string result; + for (unsigned i = 0; i < 32; ++i) + { + if (m_value & (1U << i)) + { + result += ',' + std::to_string(i + 1); + } + } + + return result.substr(1); + } + + void CpuAffinity::setValues(const std::vector& values) + { + if (values.empty()) + { + throw ParsingError("empty list is not allowed"); + } + + if (1 == values.size()) + { + if ("app" == values.front()) + { + m_value = 0; + return; + } + else if ("all" == values.front()) + { + m_value = UINT_MAX; + return; + } + } + + unsigned result = 0; + for (const auto& value : values) + { + auto num = Parser::parseUnsigned(value); + if (num < 1 || num > 32) + { + throw ParsingError("'" + value + "' is not an integer between 1 and 32"); + } + result |= 1U << (num - 1); + } + + m_value = result; + } + } +} diff --git a/DDrawCompat/Config/Settings/CpuAffinity.h b/DDrawCompat/Config/Settings/CpuAffinity.h new file mode 100644 index 0000000..63f39b2 --- /dev/null +++ b/DDrawCompat/Config/Settings/CpuAffinity.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace Config +{ + namespace Settings + { + class CpuAffinity : public ListSetting + { + public: + CpuAffinity(); + + unsigned get() const { return m_value; } + + private: + std::string getValueStr() const override; + void setValues(const std::vector& values) override; + + unsigned m_value; + }; + } +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 5bc1c0e..d6fecd7 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -179,8 +179,10 @@ + + @@ -277,8 +279,10 @@ + + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 3c507b0..4b8c187 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -85,6 +85,9 @@ {bdde0d15-27ab-43a7-9c0a-294df6a52bad} + + {0b4eec6c-ece5-4417-913c-8829b34ec08c} + @@ -408,6 +411,12 @@ Header Files\Win32 + + Header Files\Config + + + Header Files\Config\Settings + @@ -638,9 +647,15 @@ Source Files\Config + + Source Files\Config + Source Files\Win32 + + Source Files\Config\Settings + diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index a07a669..bc23ad7 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -212,7 +212,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) Win32::Thread::installHooks(); Compat::closeDbgEng(); - SetProcessAffinityMask(GetCurrentProcess(), 1); timeBeginPeriod(1); setDpiAwareness(); SetThemeAppProperties(0); diff --git a/DDrawCompat/Win32/Thread.cpp b/DDrawCompat/Win32/Thread.cpp index 9409925..d931cb4 100644 --- a/DDrawCompat/Win32/Thread.cpp +++ b/DDrawCompat/Win32/Thread.cpp @@ -7,6 +7,16 @@ namespace { + BOOL WINAPI setProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask) + { + LOG_FUNC("SetProcessAffinityMask", hProcess, Compat::hex(dwProcessAffinityMask)); + if (0 != Config::cpuAffinity.get()) + { + return LOG_RESULT(TRUE); + } + return LOG_RESULT(CALL_ORIG_FUNC(SetProcessAffinityMask)(hProcess, dwProcessAffinityMask)); + } + BOOL WINAPI setProcessPriorityBoost(HANDLE hProcess, BOOL bDisablePriorityBoost) { LOG_FUNC("SetProcessPriorityBoost", hProcess, bDisablePriorityBoost); @@ -34,6 +44,22 @@ namespace Win32 { void applyConfig() { + auto cpuAffinity = Config::cpuAffinity.get(); + if (0 != cpuAffinity) + { + SYSTEM_INFO si = {}; + GetSystemInfo(&si); + const unsigned cpuCount = min(si.dwNumberOfProcessors, 32); + cpuAffinity &= UINT_MAX >> (32 - cpuCount); + + if (0 == cpuAffinity || !CALL_ORIG_FUNC(SetProcessAffinityMask)(GetCurrentProcess(), cpuAffinity)) + { + Compat::Log() << (0 == cpuAffinity ? "Invalid" : "Failed to set") << " CPU affinity, falling back to default"; + Config::cpuAffinity.reset(); + CALL_ORIG_FUNC(SetProcessAffinityMask)(GetCurrentProcess(), Config::cpuAffinity.get()); + } + } + switch (Config::threadPriorityBoost.get()) { case Config::Settings::ThreadPriorityBoost::OFF: @@ -53,6 +79,7 @@ namespace Win32 void installHooks() { + HOOK_FUNCTION(kernel32, SetProcessAffinityMask, setProcessAffinityMask); HOOK_FUNCTION(kernel32, SetProcessPriorityBoost, setProcessPriorityBoost); HOOK_FUNCTION(kernel32, SetThreadPriorityBoost, setThreadPriorityBoost); }