fix(audio): pace blocking audio calls with a high-resolution timer
On Windows, nanosleep and this_thread::sleep_for/sleep_until go through winpthreads, which ends a wait on a 15.6 ms GetTickCount64 step. Blocking sceAudioOutOutput and sceAudioOut2ContextPush calls returned in bursts about every 16 ms, a blocking sceAudio3dPortPush ran at about 68 % of real time, and sceAudioInInput without a device woke anywhere from 1 ms to 31 ms apart. They now sleep with PreciseSleepUs or the new PreciseSleepUntil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
787239eca5
commit
b2cde4a0f2
|
|
@ -7,9 +7,9 @@
|
|||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/PreciseWait.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ int APS5_VABI sceAudio3dPortPush(uint32_t port_id, uint32_t blocking) {
|
|||
if (blocking == AUDIO3D_BLOCKING_ASYNC || level < g_port.queue_depth) return 0;
|
||||
wait_until = g_port.playing[level - g_port.queue_depth];
|
||||
}
|
||||
while (Clock::now() < wait_until) std::this_thread::sleep_until(wait_until);
|
||||
PreciseSleepUntil(wait_until);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/PreciseWait.hpp"
|
||||
#include "SDL.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ Port* find(int handle) {
|
|||
}
|
||||
|
||||
std::size_t capture(SDL_AudioDeviceID device, std::uint8_t* dest, std::size_t bytes, Clock::time_point deadline) {
|
||||
while (SDL_GetQueuedAudioSize(device) < bytes && Clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
while (SDL_GetQueuedAudioSize(device) < bytes && Clock::now() < deadline) PreciseSleepUs(1000);
|
||||
const std::size_t queued = SDL_GetQueuedAudioSize(device);
|
||||
std::array<std::uint8_t, 4096> discard;
|
||||
for (std::size_t excess = queued > bytes * MAX_QUEUED_BLOCKS ? queued - bytes * MAX_QUEUED_BLOCKS : 0; excess > 0;) {
|
||||
|
|
@ -122,7 +122,7 @@ int APS5_VABI sceAudioInInput(int handle, void* dest) {
|
|||
if (device != 0) {
|
||||
captured = capture(device, out, bytes, deadline);
|
||||
} else {
|
||||
while (Clock::now() < wake) std::this_thread::sleep_until(wake);
|
||||
PreciseSleepUntil(wake);
|
||||
}
|
||||
std::memset(out + captured, 0, bytes - captured);
|
||||
lock.lock();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "SDL.h"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/PreciseWait.hpp"
|
||||
#include "prx/libkernel/Time/include/Time.hpp"
|
||||
|
||||
static constexpr int PORT_TYPE_MAIN = 0;
|
||||
|
|
@ -192,11 +193,8 @@ static const void* prepareBuffer(const Port& port, const void* data, std::vector
|
|||
}
|
||||
|
||||
static void sleepUs(std::unique_lock<std::mutex>& lock, std::uint64_t us) {
|
||||
struct timespec req{};
|
||||
req.tv_sec = static_cast<time_t>(us / 1000000ULL);
|
||||
req.tv_nsec = static_cast<long>((us % 1000000ULL) * 1000ULL);
|
||||
lock.unlock();
|
||||
nanosleep(&req, nullptr);
|
||||
PreciseSleepUs(us);
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <vector>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/PreciseWait.hpp"
|
||||
#include "AudioOut2Internal.hpp"
|
||||
|
||||
// An AudioOut2 context is the hardware output queue: every push appends a grain (num_grains samples)
|
||||
|
|
@ -324,7 +325,7 @@ int APS5_VABI sceAudioOut2ContextPush(AudioOut2ContextHandle ctx, uint32_t block
|
|||
}
|
||||
if (now - waitStart > FULL_WAIT_TIMEOUT) break;
|
||||
lock.unlock();
|
||||
std::this_thread::sleep_for(FULL_WAIT_STEP);
|
||||
PreciseSleepUs(std::chrono::microseconds(FULL_WAIT_STEP).count());
|
||||
lock.lock();
|
||||
now = Clock::now();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,4 +37,11 @@ inline void PreciseSleepUs(unsigned long long micros) {
|
|||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
}
|
||||
|
||||
template <class TClock, class TDuration>
|
||||
inline void PreciseSleepUntil(const std::chrono::time_point<TClock, TDuration>& deadline) {
|
||||
for (auto now = TClock::now(); now < deadline; now = TClock::now()) {
|
||||
PreciseSleepUs(static_cast<unsigned long long>(std::chrono::ceil<std::chrono::microseconds>(deadline - now).count()));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue