mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-26 17:12:03 +00:00
[TEST] Rework Android CPU affinity/ threading
This commit is contained in:
+1
-1
@@ -594,7 +594,7 @@ abstract class SettingsItem(
|
|||||||
IntSetting.ANDROID_PIPELINE_WORKERS,
|
IntSetting.ANDROID_PIPELINE_WORKERS,
|
||||||
titleId = R.string.pipeline_worker_cores,
|
titleId = R.string.pipeline_worker_cores,
|
||||||
descriptionId = R.string.pipeline_worker_cores_description,
|
descriptionId = R.string.pipeline_worker_cores_description,
|
||||||
min = 1,
|
min = 2,
|
||||||
max = 8,
|
max = 8,
|
||||||
units = "cores"
|
units = "cores"
|
||||||
)
|
)
|
||||||
|
|||||||
+98
-21
@@ -39,6 +39,81 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_AUDIO = -19;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_AUDIO = -16;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_DISPLAY = -8;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DISPLAY = -4;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_FOREGROUND = -2;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_MORE_FAVORABLE = -1;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DEFAULT = 0;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LESS_FAVORABLE = 1;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_BACKGROUND = 10;
|
||||||
|
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LOWEST = 19;
|
||||||
|
|
||||||
|
constexpr size_t ANDROID_MINIMUM_PERFORMANCE_CORES = 4;
|
||||||
|
|
||||||
|
cpu_set_t ComputePerformanceCoreMask() {
|
||||||
|
cpu_set_t mask;
|
||||||
|
CPU_ZERO(&mask);
|
||||||
|
|
||||||
|
cpu_set_t allowed;
|
||||||
|
CPU_ZERO(&allowed);
|
||||||
|
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<long, int>> cores;
|
||||||
|
const int total = static_cast<int>(std::thread::hardware_concurrency());
|
||||||
|
for (int cpu = 0; cpu < total; ++cpu) {
|
||||||
|
if (!CPU_ISSET(cpu, &allowed)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long max_frequency = 0;
|
||||||
|
std::ifstream file("/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
|
||||||
|
"/cpufreq/cpuinfo_max_freq");
|
||||||
|
if (!file || !(file >> max_frequency) || max_frequency <= 0) {
|
||||||
|
CPU_ZERO(&mask);
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
cores.emplace_back(max_frequency, cpu);
|
||||||
|
}
|
||||||
|
if (cores.empty()) {
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(cores.begin(), cores.end(),
|
||||||
|
[](const auto& lhs, const auto& rhs) { return lhs.first > rhs.first; });
|
||||||
|
|
||||||
|
size_t taken = 0;
|
||||||
|
long cluster_frequency = cores.front().first;
|
||||||
|
for (const auto& [frequency, cpu] : cores) {
|
||||||
|
if (frequency != cluster_frequency) {
|
||||||
|
if (taken >= ANDROID_MINIMUM_PERFORMANCE_CORES) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cluster_frequency = frequency;
|
||||||
|
}
|
||||||
|
CPU_SET(cpu, &mask);
|
||||||
|
++taken;
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpu_set_t& PerformanceCoreMask() {
|
||||||
|
static const cpu_set_t mask = ComputePerformanceCoreMask();
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "common/cpu_features.h"
|
#include "common/cpu_features.h"
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -78,6 +153,21 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
set_thread_priority(find_thread(NULL), priority);
|
set_thread_priority(find_thread(NULL), priority);
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
const int nice_value = [&]() {
|
||||||
|
switch (new_priority) {
|
||||||
|
case ThreadPriority::Low: return ANDROID_THREAD_PRIORITY_BACKGROUND;
|
||||||
|
case ThreadPriority::Normal: return ANDROID_THREAD_PRIORITY_DEFAULT;
|
||||||
|
case ThreadPriority::High: return ANDROID_THREAD_PRIORITY_DISPLAY;
|
||||||
|
case ThreadPriority::VeryHigh: return ANDROID_THREAD_PRIORITY_URGENT_DISPLAY;
|
||||||
|
case ThreadPriority::Critical: return ANDROID_THREAD_PRIORITY_AUDIO;
|
||||||
|
default: return ANDROID_THREAD_PRIORITY_DEFAULT;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
if (setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), nice_value) != 0) {
|
||||||
|
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
|
||||||
|
GetLastErrorMsg());
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t this_thread = pthread_self();
|
pthread_t this_thread = pthread_self();
|
||||||
const auto scheduling_type = SCHED_OTHER;
|
const auto scheduling_type = SCHED_OTHER;
|
||||||
@@ -132,29 +222,16 @@ void SetCurrentThreadName(const char* name) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinCurrentThreadToPerformanceCore(size_t core_id) {
|
void SetCurrentThreadToPerformanceCores() {
|
||||||
ASSERT(core_id < 4);
|
|
||||||
// If we set a flag for a CPU that doesn't exist, the thread may not be allowed to
|
|
||||||
// run in ANY processor!
|
|
||||||
auto const total_cores = std::thread::hardware_concurrency();
|
|
||||||
if (core_id < total_cores) {
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
cpu_set_t set;
|
const cpu_set_t& mask = PerformanceCoreMask();
|
||||||
CPU_ZERO(&set);
|
if (CPU_COUNT(&mask) == 0) {
|
||||||
CPU_SET(core_id, &set);
|
return;
|
||||||
sched_setaffinity(pthread_self(), sizeof(set), &set);
|
|
||||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
|
||||||
cpu_set_t set;
|
|
||||||
CPU_ZERO(&set);
|
|
||||||
CPU_SET(core_id, &set);
|
|
||||||
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
DWORD set = 1UL << core_id;
|
|
||||||
SetThreadAffinityMask(GetCurrentThread(), set);
|
|
||||||
#else
|
|
||||||
// No pin functionality implemented
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
|
||||||
|
LOG_DEBUG(Common, "Could not restrict thread to performance cores: {}", GetLastErrorMsg());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
|||||||
+1
-1
@@ -101,6 +101,6 @@ enum class ThreadPriority : u32 {
|
|||||||
|
|
||||||
void SetCurrentThreadPriority(ThreadPriority new_priority);
|
void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||||
void SetCurrentThreadName(const char* name);
|
void SetCurrentThreadName(const char* name);
|
||||||
void PinCurrentThreadToPerformanceCore(size_t core_id);
|
void SetCurrentThreadToPerformanceCores();
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -174,12 +174,7 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
|||||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||||
Common::SetCurrentThreadName(name.c_str());
|
Common::SetCurrentThreadName(name.c_str());
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||||
#ifdef __ANDROID__
|
Common::SetCurrentThreadToPerformanceCores();
|
||||||
// Aimed specifically for Snapdragon 8 Elite devices
|
|
||||||
// This kills performance on desktop, but boosts perf for UMA devices
|
|
||||||
// like the S8E. Mediatek and Mali likely won't suffer.
|
|
||||||
Common::PinCurrentThreadToPerformanceCore(core);
|
|
||||||
#endif
|
|
||||||
auto& data = core_data[core];
|
auto& data = core_data[core];
|
||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
|||||||
thread = std::jthread([&](std::stop_token stop_token) {
|
thread = std::jthread([&](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("GPU");
|
Common::SetCurrentThreadName("GPU");
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||||
|
Common::SetCurrentThreadToPerformanceCores();
|
||||||
system.RegisterHostThread();
|
system.RegisterHostThread();
|
||||||
|
|
||||||
auto current_context = context.Acquire();
|
auto current_context = context.Acquire();
|
||||||
|
|||||||
@@ -305,8 +305,12 @@ size_t GetTotalPipelineWorkers() {
|
|||||||
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
|
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
const int configured = AndroidSettings::values.pipeline_worker_count.GetValue();
|
const int configured = AndroidSettings::values.pipeline_worker_count.GetValue();
|
||||||
const size_t desired = static_cast<size_t>(std::max(configured, 1));
|
const int clamped = std::clamp(configured, 2, 8);
|
||||||
return std::min<size_t>(max_core_threads, desired);
|
const size_t desired = static_cast<size_t>(clamped);
|
||||||
|
if (desired == 0) {
|
||||||
|
return 1ULL;
|
||||||
|
}
|
||||||
|
return std::min(max_core_threads, desired);
|
||||||
#else
|
#else
|
||||||
return max_core_threads;
|
return max_core_threads;
|
||||||
#endif
|
#endif
|
||||||
@@ -737,7 +741,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
|||||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||||
bool build_in_parallel) try {
|
bool build_in_parallel) try {
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
LOG_INFO(Render_Vulkan, "0x{:016x}", hash);
|
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
||||||
size_t env_index{0};
|
size_t env_index{0};
|
||||||
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
||||||
const bool uses_vertex_a{key.unique_hashes[0] != 0};
|
const bool uses_vertex_a{key.unique_hashes[0] != 0};
|
||||||
@@ -900,11 +904,11 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
|||||||
PipelineStatistics* statistics, bool build_in_parallel) try {
|
PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
if (device.HasBrokenCompute()) {
|
if (device.HasBrokenCompute()) {
|
||||||
LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", hash);
|
LOG_ERROR(Render_Vulkan, "Skipping {:#016x}", hash);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "0x{:016x}", hash);
|
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
||||||
|
|
||||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||||
|
|
||||||
@@ -921,7 +925,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
|||||||
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
||||||
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - clamping",
|
"Compute shader {:#016x} requests {}KB shared memory but device max is {}KB - clamping",
|
||||||
key.unique_hash,
|
key.unique_hash,
|
||||||
program.shared_memory_size / 1024,
|
program.shared_memory_size / 1024,
|
||||||
max_shared_memory / 1024);
|
max_shared_memory / 1024);
|
||||||
|
|||||||
Reference in New Issue
Block a user