mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
present/audio: menu 4:3 with natural bars; SDL3 stream-rate fix; adaptive overclock removed
- Adaptive overclock REMOVED (user decision, and correctly so): trading overclock down preserved vblank cadence but starved the game's render loop (~30-50 real frames/s under a 110+ vblank counter) and modulated the guest-time ratio continuously — an audible CDDA warble. The DuckStation-standard model stands: static user overclock; a too-slow host slows everything uniformly and audio underruns, never pitch-shifts. - SDL3 audio: psx_sdl_audio_open reported the DEVICE format instead of the stream's INPUT spec. SDL3 converts fed data FROM the requested spec; telling the DRC host_rate=48000 while SDL consumed it as 44100 was a constant ~0.92x pitch-down plus perpetual ring overfill (overflow_drops clicks) on every SDL3 build. Report the input spec — the DRC stays the only resampler. (Audio-clocking audit confirmed the bridge already implements the DuckStation contract: guest-clocked production at 768 cycles/sample, device-rate pull, +-0.5% trim, hold+fade on underrun; production stages must not be touched — netplay/savestate invariants at main.cpp:3009/3071.) - Present-aspect scene switch: with the widescreen mod engaged, menus/2D (scene gate inactive, 45-frame hysteresis in gpu.c) present at the game's own 4:3 so its NATURAL black bars stay, framed by the bezels; gameplay presents at the mod aspect. New export psx_ws_scene_wide_active(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -368,6 +368,10 @@ int gpu_ws_present_native_43(void) {
|
||||
|
||||
/* Squash applies only when configured AND the frame is being stretched. */
|
||||
static int ws_active(void) { return ws_configured() && !gpu_ws_present_native_43(); }
|
||||
/* Exported scene gate for the present-aspect switch (main.cpp): menus/2D and
|
||||
* FMV present at the game's own 4:3 (its natural bars intact, bezels around);
|
||||
* gameplay presents at the mod aspect. */
|
||||
int psx_ws_scene_wide_active(void) { return ws_active() && ws_game_mode(); }
|
||||
|
||||
/* ----- Native-wide (mode 2) ------------------------------------------------
|
||||
* Render the wider FOV into an actually wider frame instead of squashing it.
|
||||
|
||||
+17
-63
@@ -6635,69 +6635,7 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
load_transition_note(cdrom_data_read_active(), logical_load_active,
|
||||
turbo_loads_active, load_run_value);
|
||||
|
||||
/* Adaptive overclock: pin GUEST TIME to 1.0x wall. The configured
|
||||
* cpu_overclock is a CEILING; when the emu thread cannot retire that many
|
||||
* scaled cycles per wall vblank the guest timeline dilates — CD audio,
|
||||
* SIO pad exchange, and every guest timer slow together while per-vblank
|
||||
* game logic runs on whatever vblanks do arrive (measured live: CDDA at
|
||||
* 0.4x with gameplay 1.6x fast). Trading overclock DOWN in heavy scenes
|
||||
* keeps guest time (and so vblank cadence, CDDA, inputs) at wall rate;
|
||||
* headroom brings it back up toward the ceiling. Proportional controller
|
||||
* on an EMA of the wall vblank period, adjusted at most every 8 vblanks,
|
||||
* frozen during intentional unpaced windows (turbo loads / HLE boot).
|
||||
* PSX_ADAPTIVE_OC=0 disables; =N sets the floor percent (default 200). */
|
||||
{
|
||||
static int aoc_enabled = -1;
|
||||
static uint32_t aoc_floor = 200, aoc_ceiling = 0;
|
||||
static double aoc_ema = 0.0;
|
||||
static Uint64 aoc_last_pc = 0;
|
||||
static int aoc_cool = 0;
|
||||
if (aoc_enabled < 0) {
|
||||
const char *e = std::getenv("PSX_ADAPTIVE_OC");
|
||||
aoc_enabled = 1;
|
||||
if (e && *e) {
|
||||
int v = atoi(e);
|
||||
if (v == 0) aoc_enabled = 0;
|
||||
else if (v >= 100) aoc_floor = (uint32_t)v;
|
||||
}
|
||||
aoc_ceiling = psx_get_cpu_overclock();
|
||||
if (aoc_ceiling <= aoc_floor) aoc_enabled = 0;
|
||||
if (aoc_enabled)
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: adaptive overclock armed (floor %u%%, ceiling %u%%)\n",
|
||||
aoc_floor, aoc_ceiling);
|
||||
}
|
||||
if (aoc_enabled && g_frame_period_ms > 0.0 && !turbo_loads_active &&
|
||||
!psx_netplay_active()) {
|
||||
Uint64 pc_now = SDL_GetPerformanceCounter();
|
||||
if (aoc_last_pc) {
|
||||
double dt = (double)(pc_now - aoc_last_pc) * 1000.0 /
|
||||
(double)SDL_GetPerformanceFrequency();
|
||||
if (dt > 0.0 && dt < 250.0) {
|
||||
aoc_ema = (aoc_ema > 0.0) ? aoc_ema * 0.85 + dt * 0.15 : dt;
|
||||
if (++aoc_cool >= 8 && aoc_ema > 0.0) {
|
||||
aoc_cool = 0;
|
||||
double r = aoc_ema / g_frame_period_ms;
|
||||
uint32_t cur = psx_get_cpu_overclock();
|
||||
if (r > 1.04) {
|
||||
uint32_t n = (uint32_t)((double)cur / r);
|
||||
if (n < aoc_floor) n = aoc_floor;
|
||||
if (n < cur) psx_set_cpu_overclock(n);
|
||||
} else if (r < 0.99 && cur < aoc_ceiling) {
|
||||
uint32_t n = cur + (aoc_ceiling - cur + 9u) / 10u;
|
||||
if (n > aoc_ceiling) n = aoc_ceiling;
|
||||
psx_set_cpu_overclock(n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
aoc_ema = 0.0; /* pause/hitch: relearn */
|
||||
}
|
||||
}
|
||||
aoc_last_pc = pc_now;
|
||||
} else {
|
||||
aoc_last_pc = 0; /* frozen window: do not learn across it */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* FMV auto-skip ([video] auto_skip_fmv). A streaming FMV is XA audio + MDEC
|
||||
* video together. Detect "MDEC produced a frame since the last vblank AND XA
|
||||
@@ -6968,6 +6906,22 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
refresh_widescreen_projection();
|
||||
}
|
||||
|
||||
/* Present-aspect scene switch: menus/2D (widescreen scene gate inactive)
|
||||
* present at the game's own 4:3 so its NATURAL black bars stay, framed by
|
||||
* the bezels; gameplay presents at the mod aspect. The gpu.c gate carries
|
||||
* the 45-frame hysteresis and the bezel fade covers the transition. */
|
||||
if (g_ws_engaged) {
|
||||
extern int psx_ws_scene_wide_active(void);
|
||||
static int s_last_present_wide = -1;
|
||||
int wide_now = psx_ws_scene_wide_active() ? 1 : 0;
|
||||
if (wide_now != s_last_present_wide) {
|
||||
s_last_present_wide = wide_now;
|
||||
gl_renderer_set_display_aspect(
|
||||
wide_now ? g_video_aspect_num : 4,
|
||||
wide_now ? g_video_aspect_den : 3);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rollback resim (§33/§47): short catch-up keeps hold-last; long catch-up
|
||||
* periodically presents live Replay VRAM so the display shows progress
|
||||
* while sim stays uncapped. TipHold invent-cap stall uses hold-last from
|
||||
|
||||
@@ -46,13 +46,18 @@ extern "C" SDL_AudioDeviceID psx_sdl_audio_open(
|
||||
}
|
||||
|
||||
const SDL_AudioDeviceID device = SDL_GetAudioStreamDevice(s_audio_stream);
|
||||
SDL_AudioSpec obtained = requested;
|
||||
(void)SDL_GetAudioDeviceFormat(device, &obtained, nullptr);
|
||||
if (have) {
|
||||
/* Report the STREAM'S INPUT spec, not the device format: SDL3
|
||||
* converts whatever we feed FROM `requested` TO the device rate
|
||||
* internally. Reporting the device rate here made the DRC render
|
||||
* at (typically) 48 kHz intent while SDL re-resampled it as
|
||||
* 44.1 kHz — a constant ~0.92x pitch-down on every SDL3 build plus
|
||||
* a perpetual ring overfill drained by overflow_drops clicks. The
|
||||
* DRC is the only resampler; SDL3 must see honest 44.1 kHz input. */
|
||||
std::memset(have, 0, sizeof(*have));
|
||||
have->freq = obtained.freq;
|
||||
have->format = obtained.format;
|
||||
have->channels = obtained.channels;
|
||||
have->freq = requested.freq;
|
||||
have->format = requested.format;
|
||||
have->channels = requested.channels;
|
||||
have->samples = want->samples;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user