mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
Core, VideoCommon: drop std::this_thread waits
Waits that polled now block on an eventcount that the waited-on thread notifies: - Booting in dual core waits for the CPU thread to reach its run loop instead of polling every millisecond. - AsyncShaderCompiler::WaitUntilCompletion wakes as each worker finishes an item instead of every 33 ms. Timed sleeps go through Common::SleepUntil and SleepCurrentThreadUs, built on retro_sleep_us: the CoreTiming throttle, PrecisionTimer, real Wiimote report pacing, Bluetooth timed transfers, the Linux L2CAP connect retry and the BBA read thread's idle wait. FrameTiming::ThrottleFrame had no callers and is deleted.
This commit is contained in:
1 parent
f7d5095739
commit
cb214d1034
16 files changed
+82
-58
No files matched your search
@@ -44,6 +44,12 @@ void SleepCurrentThread(int ms)
|
||||
retro_sleep(ms);
|
||||
}
|
||||
|
||||
void SleepCurrentThreadUs(long long us)
|
||||
{
|
||||
if (us > 0)
|
||||
retro_sleep_us((unsigned)us);
|
||||
}
|
||||
|
||||
void YieldCPU()
|
||||
{
|
||||
sthread_yield();
|
||||
|
||||
@@ -25,6 +25,10 @@ int CurrentThreadId();
|
||||
|
||||
void SleepCurrentThread(int ms);
|
||||
|
||||
// Sleeps for us microseconds; returns at once when us is not positive. Callers that
|
||||
// sleep until a time point pass the distance to it.
|
||||
void SleepCurrentThreadUs(long long us);
|
||||
|
||||
// Use this function during a spin-wait to make the current thread
|
||||
// relax while another thread is working. This may be more efficient
|
||||
// than using events because event functions use kernel calls.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -152,6 +152,12 @@ PrecisionTimer::~PrecisionTimer()
|
||||
#endif
|
||||
}
|
||||
|
||||
void SleepUntil(TimePoint target)
|
||||
{
|
||||
SleepCurrentThreadUs(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(target - Clock::now()).count());
|
||||
}
|
||||
|
||||
void PrecisionTimer::SleepUntil(Clock::time_point target)
|
||||
{
|
||||
constexpr auto SPIN_TIME =
|
||||
@@ -179,7 +185,7 @@ void PrecisionTimer::SleepUntil(Clock::time_point target)
|
||||
}
|
||||
#else
|
||||
// Sleeping on Linux generally isn't as terrible as it is on Windows.
|
||||
std::this_thread::sleep_until(target - SPIN_TIME);
|
||||
Common::SleepUntil(target - SPIN_TIME);
|
||||
#endif
|
||||
|
||||
// Spin for the remaining time.
|
||||
@@ -188,7 +194,7 @@ void PrecisionTimer::SleepUntil(Clock::time_point target)
|
||||
#if defined(_WIN32)
|
||||
YieldProcessor();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
YieldCPU();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ private:
|
||||
bool m_running{false};
|
||||
};
|
||||
|
||||
// Sleeps until target, with the OS sleep's granularity; returns at once if it has passed.
|
||||
void SleepUntil(TimePoint target);
|
||||
|
||||
class PrecisionTimer
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -435,7 +434,7 @@ void CoreTimingManager::SleepUntil(TimePoint time_point)
|
||||
if (use_precision_timer)
|
||||
m_precision_cpu_timer.SleepUntil(time_point);
|
||||
else
|
||||
std::this_thread::sleep_until(time_point);
|
||||
Common::SleepUntil(time_point);
|
||||
|
||||
// Count amount of time sleeping for performance metrics
|
||||
const TimePoint time_after_sleep = Clock::now();
|
||||
@@ -446,7 +445,7 @@ void CoreTimingManager::SleepUntil(TimePoint time_point)
|
||||
if (use_precision_timer)
|
||||
m_precision_gpu_timer.SleepUntil(time_point);
|
||||
else
|
||||
std::this_thread::sleep_until(time_point);
|
||||
Common::SleepUntil(time_point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ CPUManager::CPUManager(Core::System& system) : m_system(system)
|
||||
retro_eventcount_init(&m_cpu_idle);
|
||||
#ifdef __LIBRETRO__
|
||||
retro_atomic_int_init(&m_cpu_run_state_reached, 0);
|
||||
retro_eventcount_init(&m_run_state_event);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -52,6 +53,9 @@ CPUManager::~CPUManager()
|
||||
node = node->next;
|
||||
delete job;
|
||||
}
|
||||
#ifdef __LIBRETRO__
|
||||
retro_eventcount_free(&m_run_state_event);
|
||||
#endif
|
||||
retro_eventcount_free(&m_cpu_idle);
|
||||
retro_eventcount_free(&m_cpu_wake);
|
||||
retro_eventcount_free(&m_stepping_released);
|
||||
@@ -205,6 +209,7 @@ void CPUManager::Run()
|
||||
|
||||
#ifdef __LIBRETRO__
|
||||
retro_atomic_store_release_int(&m_cpu_run_state_reached, 1);
|
||||
retro_eventcount_notify(&m_run_state_event);
|
||||
#endif
|
||||
while (LoadState() != State::PowerDown)
|
||||
{
|
||||
|
||||
@@ -113,6 +113,20 @@ public:
|
||||
{
|
||||
return retro_atomic_load_acquire_int(&m_cpu_run_state_reached) != 0;
|
||||
}
|
||||
// Blocks until the CPU thread has entered its run loop.
|
||||
void WaitForCPURunState()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
const int key = retro_eventcount_prepare_wait(&m_run_state_event);
|
||||
if (HasCPURunStateBeenReached())
|
||||
{
|
||||
retro_eventcount_cancel_wait(&m_run_state_event);
|
||||
return;
|
||||
}
|
||||
retro_eventcount_commit_wait(&m_run_state_event, key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
struct Job
|
||||
@@ -167,6 +181,7 @@ private:
|
||||
Core::System& m_system;
|
||||
#ifdef __LIBRETRO__
|
||||
retro_atomic_int_t m_cpu_run_state_reached;
|
||||
retro_eventcount_t m_run_state_event;
|
||||
#endif
|
||||
};
|
||||
} // namespace CPU
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include "SFML/Network/IpAddress.hpp"
|
||||
#include "SFML/Network/Socket.hpp"
|
||||
|
||||
@@ -760,7 +759,7 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(void* data)
|
||||
if (datasize == 0)
|
||||
{
|
||||
// Make thread less CPU hungry
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
Common::SleepCurrentThread(1);
|
||||
}
|
||||
|
||||
if (!self->m_read_enabled.IsSet())
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Network.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/UnixUtil.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
|
||||
@@ -313,7 +314,7 @@ bool WiimoteLinux::ConnectInternal()
|
||||
Common::LastStrerrorString());
|
||||
// A socket state is unspecified after connect() fails, so close and recreate it.
|
||||
close(descriptor);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{500});
|
||||
Common::SleepCurrentThread(500);
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <queues/mpsc_stack.h>
|
||||
@@ -19,6 +18,7 @@
|
||||
#include "Common/Quiesce.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/WiimoteSettings.h"
|
||||
@@ -438,7 +438,7 @@ bool Wiimote::Write(const WriteRecord& record)
|
||||
}
|
||||
|
||||
// Write the report at the proper time, mainly for speaker data, not that it will help much.
|
||||
std::this_thread::sleep_until(record.time);
|
||||
Common::SleepUntil(record.time);
|
||||
int ret = IOWrite(record.data, record.size);
|
||||
|
||||
return ret != 0;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@@ -11,7 +12,6 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <libusb.h>
|
||||
@@ -410,7 +410,7 @@ void LibUSBBluetoothAdapter::SubmitTimedTransfer(TimedTransfer transfer)
|
||||
if (Config::Get(Config::MAIN_PRECISION_FRAME_TIMING))
|
||||
m_precision_timer.SleepUntil(transfer.target_time);
|
||||
else
|
||||
std::this_thread::sleep_until(transfer.target_time);
|
||||
Common::SleepUntil(transfer.target_time);
|
||||
|
||||
SubmitTransfer(transfer.transfer);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <libretro.h>
|
||||
#include "Audio.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "AudioCommon/SoundStream.h"
|
||||
#include "VideoCommon/Present.h"
|
||||
#include "DolphinLibretro/Common/Globals.h"
|
||||
@@ -304,7 +303,6 @@ namespace FrameTiming
|
||||
bool g_have_frame_time_cb {false};
|
||||
|
||||
static retro_frame_time_callback ftcb = {};
|
||||
static auto last_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
void Reset()
|
||||
{
|
||||
@@ -340,7 +338,6 @@ namespace FrameTiming
|
||||
return;
|
||||
}
|
||||
|
||||
last_frame_time = std::chrono::steady_clock::now();
|
||||
DEBUG_LOG_FMT(VIDEO, "frame timing enabled: target={} usec ({} Hz)",
|
||||
ftcb.reference, refresh_rate);
|
||||
|
||||
@@ -375,34 +372,6 @@ namespace FrameTiming
|
||||
// Environment call not supported
|
||||
Libretro::Audio::g_is_fast_forwarding = false;
|
||||
}
|
||||
|
||||
void ThrottleFrame()
|
||||
{
|
||||
if (!g_have_frame_time_cb)
|
||||
return;
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
now - last_frame_time).count();
|
||||
|
||||
retro_usec_t target_us = target_frame_duration_usec.load(std::memory_order_relaxed);
|
||||
|
||||
if (elapsed_us < target_us)
|
||||
{
|
||||
auto sleep_us = target_us - elapsed_us;
|
||||
if (sleep_us > 1000)
|
||||
{
|
||||
Common::SleepCurrentThread((sleep_us - 500) / 1000);
|
||||
}
|
||||
|
||||
while (std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - last_frame_time).count() < target_us)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
last_frame_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
} // namespace FrameTiming
|
||||
} // namespace Libretro
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace FrameTiming
|
||||
bool IsEnabled();
|
||||
void CheckForFastForwarding();
|
||||
bool IsFastForwarding();
|
||||
void ThrottleFrame();
|
||||
} // namespace FrameTiming
|
||||
|
||||
} // namespace Libretro
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <cstdint>
|
||||
#include <libretro.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
@@ -291,9 +290,7 @@ void retro_run(void)
|
||||
Core::StartEmuThread(system, std::move(Core::g_boot_params), wsi);
|
||||
|
||||
// Wait until CPU thread has reached Run()
|
||||
auto& cpu_manager = system.GetCPU();
|
||||
while (!cpu_manager.HasCPURunStateBeenReached())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
system.GetCPU().WaitForCPURunState();
|
||||
}
|
||||
else
|
||||
Core::EmuThread(system, std::move(Core::g_boot_params), wsi);
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
@@ -16,6 +14,7 @@ namespace VideoCommon
|
||||
{
|
||||
AsyncShaderCompiler::AsyncShaderCompiler()
|
||||
{
|
||||
retro_eventcount_init(&m_work_done);
|
||||
}
|
||||
|
||||
AsyncShaderCompiler::~AsyncShaderCompiler()
|
||||
@@ -24,6 +23,19 @@ AsyncShaderCompiler::~AsyncShaderCompiler()
|
||||
// The work item classes are expected to clean up after themselves.
|
||||
ASSERT(!HasWorkerThreads());
|
||||
ClearAllWork();
|
||||
retro_eventcount_free(&m_work_done);
|
||||
}
|
||||
|
||||
bool AsyncShaderCompiler::WaitForWorkDone(s64 timeout_us)
|
||||
{
|
||||
const int key = retro_eventcount_prepare_wait(&m_work_done);
|
||||
if (!HasPendingWork())
|
||||
{
|
||||
retro_eventcount_cancel_wait(&m_work_done);
|
||||
return true;
|
||||
}
|
||||
retro_eventcount_commit_wait_timeout(&m_work_done, key, timeout_us);
|
||||
return !HasPendingWork();
|
||||
}
|
||||
|
||||
void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item, u32 priority)
|
||||
@@ -135,14 +147,17 @@ bool AsyncShaderCompiler::WaitUntilCompletion(
|
||||
if (!HasPendingWork())
|
||||
return true;
|
||||
|
||||
// Wait a second before opening a progress dialog.
|
||||
// Wait a second before reporting progress, woken as each item finishes.
|
||||
// This way, if the operation completes quickly, we don't annoy the user.
|
||||
constexpr u32 CHECK_INTERVAL_MS = 1000 / 30;
|
||||
constexpr auto CHECK_INTERVAL = std::chrono::milliseconds(CHECK_INTERVAL_MS);
|
||||
for (u32 i = 0; i < (1000 / CHECK_INTERVAL_MS); i++)
|
||||
constexpr s64 PROGRESS_INTERVAL_US = 1000000 / 30;
|
||||
const TimePoint quiet_until = Clock::now() + std::chrono::seconds(1);
|
||||
for (;;)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(CHECK_INTERVAL));
|
||||
if (!HasPendingWork())
|
||||
const s64 left_us =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(quiet_until - Clock::now()).count();
|
||||
if (left_us <= 0)
|
||||
break;
|
||||
if (WaitForWorkDone(left_us))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -158,7 +173,7 @@ bool AsyncShaderCompiler::WaitUntilCompletion(
|
||||
const size_t remaining_items = m_pending_work.size() + ItemsInSlots();
|
||||
|
||||
progress_callback(total_items - remaining_items, total_items);
|
||||
std::this_thread::sleep_for(CHECK_INTERVAL);
|
||||
WaitForWorkDone(PROGRESS_INTERVAL_US);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -331,6 +346,7 @@ void AsyncShaderCompiler::WorkerThreadRun(Worker* worker)
|
||||
mpsc_stack_push(&m_completed_work, &done->link);
|
||||
}
|
||||
retro_atomic_fetch_sub_int(&m_busy_workers, 1);
|
||||
retro_eventcount_notify(&m_work_done);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ protected:
|
||||
virtual void WorkerThreadExit(void* param);
|
||||
|
||||
private:
|
||||
// Waits up to timeout_us for a worker to finish an item; true once nothing is pending.
|
||||
bool WaitForWorkDone(s64 timeout_us);
|
||||
|
||||
void WorkerThreadStart();
|
||||
void WorkerThreadEntryPoint(void* param);
|
||||
void WorkerThreadRun();
|
||||
@@ -107,6 +110,8 @@ private:
|
||||
// the unique_ptr be moved out.
|
||||
std::multimap<u32, WorkItemPtr> m_pending_work;
|
||||
retro_atomic_int_t m_busy_workers{};
|
||||
// Notified each time a worker finishes an item.
|
||||
retro_eventcount_t m_work_done;
|
||||
|
||||
mpsc_stack_t m_completed_work{};
|
||||
// Items finished synchronously, when there are no workers. Video thread only.
|
||||
|
||||
Reference in new issue
Block a user