Compare commits
1 Commits
chore/port
...
iterate-2A
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6acb21b2e |
@@ -2465,10 +2465,20 @@ fn coord_pre_round(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kernel.xaudio_tick_enabled {
|
if kernel.xaudio_tick_enabled {
|
||||||
if kernel.parallel_active {
|
let fired = if kernel.parallel_active {
|
||||||
kernel.xaudio.tick_wallclock();
|
kernel.xaudio.tick_wallclock()
|
||||||
} else {
|
} else {
|
||||||
kernel.xaudio.tick_instr(stats.instruction_count);
|
kernel.xaudio.tick_instr(stats.instruction_count)
|
||||||
|
};
|
||||||
|
// AUDIT-2AU Option β: on each audio period, re-signal the XAudio
|
||||||
|
// render loop's captured frame-event pair (buffer-ready /
|
||||||
|
// frame-done). Emulates canary's host XAudio2 OnBufferEnd firing
|
||||||
|
// those events every period; without it ours's render loop
|
||||||
|
// (tid=11) wedges on its second KeWait forever and starves the
|
||||||
|
// tid=9/10 mixers + tid=12 DPC downstream (2.AS cascade). Gated
|
||||||
|
// by the same instruction-count tick => deterministic.
|
||||||
|
if fired {
|
||||||
|
xenia_kernel::exports::pulse_xaudio_frame_events(kernel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5346,6 +5346,27 @@ fn emit_signal_match_if_waiters(
|
|||||||
crate::event_log::emit_signal_match(tid, cycle, signal_call, target_handle, n, &tids);
|
crate::event_log::emit_signal_match(tid, cycle, signal_call, target_handle, n, &tids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AUDIT-2AU Option β: re-signal the XAudio render loop's frame-event
|
||||||
|
/// pair, emulating the host XAudio2 OnBufferEnd callback firing once per
|
||||||
|
/// audio period. Called from the round prologue gated by the same
|
||||||
|
/// instruction-count audio cadence that drives `tick_instr`, so timing
|
||||||
|
/// is deterministic (never host_ns). Mirrors `ke_set_event`'s signal +
|
||||||
|
/// wake sequence for each captured event handle (see
|
||||||
|
/// `do_wait_multiple` capture site + `XAudioState::frame_events`).
|
||||||
|
pub fn pulse_xaudio_frame_events(state: &mut KernelState) {
|
||||||
|
if state.xaudio.frame_events.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let events = state.xaudio.frame_events.clone();
|
||||||
|
for h in events {
|
||||||
|
if let Some(KernelObject::Event { signaled, .. }) = state.objects.get_mut(&h) {
|
||||||
|
*signaled = true;
|
||||||
|
emit_signal_match_if_waiters(state, "XAudioFramePulse", h);
|
||||||
|
wake_eligible_waiters(state, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ke_set_event(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
|
fn ke_set_event(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
|
||||||
// r3 = PKEVENT on Ke* (guest pointer). See `ensure_dispatcher_object`
|
// r3 = PKEVENT on Ke* (guest pointer). See `ensure_dispatcher_object`
|
||||||
// for why we need the lazy-shadow step here.
|
// for why we need the lazy-shadow step here.
|
||||||
@@ -5652,6 +5673,30 @@ fn do_wait_multiple(
|
|||||||
Some(None) => None,
|
Some(None) => None,
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
// AUDIT-2AU Option β: capture the XAudio render loop's frame-event
|
||||||
|
// pair at the wait site. Sylpheed's render-driver thread (tid=11,
|
||||||
|
// entry 0x824d2a94 = canary tid=4) blocks here on a WaitAny over two
|
||||||
|
// guest-address Events (the "buffer ready" manual-reset + "frame
|
||||||
|
// done" auto-reset pair). In canary these are signaled every audio
|
||||||
|
// period by the host XAudio2 OnBufferEnd callback; in ours nothing
|
||||||
|
// signals them after the first fast-path consumes the auto-reset
|
||||||
|
// member, so the loop wedges forever (2.AL). Record the pair (no
|
||||||
|
// hardcoded addresses) so the round-prologue audio-cadence ticker
|
||||||
|
// re-signals them. Discriminator: a *multi*-handle WaitAny whose
|
||||||
|
// members are all guest-address Events, with at least one XAudio
|
||||||
|
// client registered — tid=2's lone guest-Event wait goes through
|
||||||
|
// do_wait_single, so this won't catch it.
|
||||||
|
if !wait_all
|
||||||
|
&& handles.len() >= 2
|
||||||
|
&& state.xaudio.any_registered()
|
||||||
|
&& handles.iter().all(|&h| {
|
||||||
|
h >= 0x8000_0000 && matches!(state.objects.get(&h), Some(KernelObject::Event { .. }))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
for &h in &handles {
|
||||||
|
state.xaudio.note_frame_event(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
let current_ref = state.scheduler.current_ref();
|
let current_ref = state.scheduler.current_ref();
|
||||||
for &h in &handles {
|
for &h in &handles {
|
||||||
handle_enqueue_waiter(state, h, current_ref);
|
handle_enqueue_waiter(state, h, current_ref);
|
||||||
|
|||||||
@@ -110,6 +110,20 @@ pub struct XAudioState {
|
|||||||
/// `xenia_cpu` (none currently) to keep this self-contained.
|
/// `xenia_cpu` (none currently) to keep this self-contained.
|
||||||
pub worker_handles: [Option<u32>; XAUDIO_MAX_CLIENTS],
|
pub worker_handles: [Option<u32>; XAUDIO_MAX_CLIENTS],
|
||||||
pub worker_refs: [Option<ThreadRef>; XAUDIO_MAX_CLIENTS],
|
pub worker_refs: [Option<ThreadRef>; XAUDIO_MAX_CLIENTS],
|
||||||
|
/// AUDIT-2AU Option β: guest-address Event handles that the XAudio
|
||||||
|
/// render-driver loop (Sylpheed tid=11, entry 0x824d2a94 = canary
|
||||||
|
/// tid=4) blocks on via `KeWaitForMultipleObjects(WaitAny)`. These
|
||||||
|
/// are the per-frame "buffer ready" / "frame done" events that, in
|
||||||
|
/// canary, are signaled by the host XAudio2 driver's OnBufferEnd
|
||||||
|
/// callback every audio period. In ours the render loop's *second*
|
||||||
|
/// KeWait blocks forever because the auto-reset member was consumed
|
||||||
|
/// by the first fast-path and the manual-reset member is never
|
||||||
|
/// signaled (2.AL: signal.match on these SIDs = 0 whole-run). We
|
||||||
|
/// discover the exact handle pair at the wait site (no hardcoded
|
||||||
|
/// guest addresses) and re-signal them at the audio cadence from the
|
||||||
|
/// round prologue so the render loop sustains. Deterministic: signal
|
||||||
|
/// timing is gated by the instruction-count ticker, never host_ns.
|
||||||
|
pub frame_events: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for XAudioState {
|
impl Default for XAudioState {
|
||||||
@@ -124,6 +138,7 @@ impl Default for XAudioState {
|
|||||||
last_instant: None,
|
last_instant: None,
|
||||||
worker_handles: [None; XAUDIO_MAX_CLIENTS],
|
worker_handles: [None; XAUDIO_MAX_CLIENTS],
|
||||||
worker_refs: [None; XAUDIO_MAX_CLIENTS],
|
worker_refs: [None; XAUDIO_MAX_CLIENTS],
|
||||||
|
frame_events: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +175,15 @@ impl XAudioState {
|
|||||||
self.clients.iter().any(|c| c.is_some())
|
self.clients.iter().any(|c| c.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AUDIT-2AU Option β: remember a guest-address Event handle the XAudio
|
||||||
|
/// render loop blocks on, so the cadence ticker can re-signal it. Dedup
|
||||||
|
/// to keep the set tiny (Sylpheed's render loop waits on exactly two).
|
||||||
|
pub fn note_frame_event(&mut self, handle: u32) {
|
||||||
|
if !self.frame_events.contains(&handle) {
|
||||||
|
self.frame_events.push(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn enqueue_all_active(&mut self) {
|
fn enqueue_all_active(&mut self) {
|
||||||
for i in 0..XAUDIO_MAX_CLIENTS {
|
for i in 0..XAUDIO_MAX_CLIENTS {
|
||||||
if self.clients[i].is_none() {
|
if self.clients[i].is_none() {
|
||||||
|
|||||||
Reference in New Issue
Block a user