• linux-x64-main-fa2616d fa2616d224

    Ghost released this 2026-07-15 08:36:20 -04:00 | 448 commits to 1016_part3 since this release

    • [macos/linux] Cross-platform host memory, TLS, and ABI layer for POSIX

    Introduces the foundation for running SharpEmu on macOS (osx-x64 under
    Rosetta 2) and Linux (linux-x64). The CPU backend executes guest x86-64
    code natively, so these targets run the whole process as x86-64; this
    commit replaces the Windows-only host primitives with platform-dispatched
    equivalents so the guest boots and services HLE calls off Windows.

    Memory (HostMemory.cs, new): a Win32-semantics facade over
    mmap/mprotect/munmap with a shadow region table answering VirtualQuery.
    PhysicalVirtualMemory, DirectExecutionBackend, StubManager, and the two
    Kernel*CompatExports now go through it instead of kernel32 P/Invokes.
    Exact-address requests use MAP_FIXED_NOREPLACE (Linux) / guarded
    MAP_FIXED (macOS) so they match Win32 "map there or fail" semantics.

    TLS + host helpers (PosixHostStubs.cs, new): pthread-backed TLS and
    Win64-ABI-compatible stubs for the kernel32 helpers the backend embeds
    into emitted x86-64 code (TlsGetValue, QueryPerformanceCounter,
    SwitchToThread, Sleep). A Win64->SysV thunk wraps managed callbacks,
    since .NET on POSIX compiles them for the SysV ABI while the emitted
    call sites use Win64.

    Guest address layout: the 0x7FFx window is Windows-only (dyld shared
    cache / Rosetta runtime live there on POSIX), so stack/TLS/stub regions
    relocate to 0x6FFx off Windows.

    Vectored exception handling is gated off on POSIX for now (guest faults
    are not yet recovered) — the signal-based bridge is the next step. Also
    adds osx-x64 to the RID list and a Docker-based Linux smoke-test script.

    Status: on both macOS (Rosetta) and Linux (amd64), the guest now boots,
    runs native x86-64 code, and dispatches HLE imports. macOS stops at a
    Rosetta translation-cache issue; Linux runs ~252 imports through C++
    static-init before hitting the missing fault handler (SIGSEGV).

    • [posix] Bridge the vectored exception handler to sigaction(SIGSEGV/SIGBUS/SIGILL)

    Guest faults on macOS/Linux previously terminated the process because the
    recovery logic in DirectExecutionBackend.Exceptions.cs was Windows-only.
    This adds a POSIX front-end that reuses the existing handler bodies:

    • DirectExecutionBackend.PosixSignals.cs installs SA_SIGINFO handlers via
      an [UnmanagedCallersOnly] entry, rebuilds the Win64 EXCEPTION_POINTERS /
      CONTEXT view from the platform mcontext (Darwin __ss thread state via
      the mcontext pointer at ucontext+48, Linux glibc gregs at ucontext+40 --
      offsets verified against the headers on both platforms), runs the same
      chain as the VEH path (TryRecoverUnresolvedSentinel trap-sentinel
      recovery, TryHandleLazyCommittedPage demand paging, VectoredHandler
      diagnostics incl. FS/GS TLS-fault detection), and writes register
      changes back into the mcontext so sigreturn resumes the repaired guest.
      Unrecovered faults chain to the previously installed handler so the
      .NET runtime keeps mapping its own faults to managed exceptions.

    • The whole recovery path is warmed up with fabricated inputs before the
      handlers are installed. This is required under Rosetta 2: the signal
      trampoline cannot enter x86 code that has never been executed (and so
      never translated) -- a cold handler is silently never invoked and the
      faulting instruction retries forever (reproduced and verified in an
      isolated .NET test under Rosetta for Linux). It also keeps first-fault
      JIT work out of the signal frame.

    • Handlers run without SA_ONSTACK: the runtime's alternate stacks are too
      small for the diagnostic path, while guest (2MB) and host thread stacks
      match where Windows dispatches exceptions anyway.

    • The raw reads in the shared fault diagnostics (stack qwords, RBP walk,
      code bytes at RIP) now probe the region table on POSIX before touching
      memory, since a nested SIGSEGV inside the handler would kill the
      process before diagnostics finish. Windows keeps its try/catch reads.

    • Escape hatches: SHARPEMU_DISABLE_POSIX_SIGNALS=1 skips installation,
      SHARPEMU_DISABLE_RAW_HANDLER=1 disables sentinel recovery (parity with
      Windows), SHARPEMU_LOG_POSIX_SIGNALS=1 traces every delivery (first 16
      and every 1024th are always traced).

    Verified with the test game: Linux (amd64 container) previously died with
    SIGSEGV right after import #252; it now recovers/diagnoses signals and the
    run proceeds to the real next blocker, an unpatched negative-offset guest
    TLS read (fault at TLS base - 0x1708), which gets the full NATIVE
    EXCEPTION dump before terminating. macOS is unchanged: the bridge installs
    and the game still stops at the known Rosetta translation-cache error at
    import 12, which is the next work item.

    • [posix] Fix guest memory layout faults: TLS prefix, exact mmap, map search base

    Three fixes that take the test game from dying during libc init to running
    its full main loop on macOS and Linux:

    • Static TLS blocks live below the TCB (FreeBSD amd64 variant II) and
      libc.prx reaches past -0x1700, but only a 4KB prefix was mapped below
      the TLS base. The prefix is now 64KB on POSIX (Windows keeps 4KB); the
      fault was a read at TLS base - 0x1708 during libc init.

    • HostMemory exact allocation on macOS used MAP_FIXED, which silently
      maps over untracked host memory. The direct-memory allocator's address
      scan walked into the .NET runtime's JIT heap and replaced live code,
      which under Rosetta 2 surfaced as "no code fragment associated with
      the given arm pc". Exact placement now passes the address as a hint
      and fails on relocation, like MAP_FIXED_NOREPLACE does on Linux.

    • sceKernelMapDirectMemory/MapFlexibleMemory searched for free space
      starting at 4GB, which is the Mach-O image base on macOS. The default
      search base is 0x20_0000_0000 on POSIX, and TryAllocateAtOrAbove now
      asks the kernel for a placement instead of page-stepping through host-
      owned address space (Rosetta ignores mmap hints for whole VA windows),
      over-allocating when the caller needs more than page alignment.

    Windows behavior is unchanged; all divergences are platform-guarded.

    • [macos] Video presenter on the main thread, MoltenVK support, window keyboard input

    Gets the test game from a headless loop to a playable window on macOS:

    • AppKit traps with SIGILL ("NSUpdateCycleInitialize() is called off the
      main thread") when GLFW runs on a worker thread. The CLI now moves
      emulation onto a worker thread on macOS and parks the real main thread
      in HostMainThread.Pump(); the presenter posts its whole window loop
      there instead of spawning a thread, and a shutdown handler asks the
      render loop to close the window so the pump unwinds on guest exit.

    • MoltenVK: enable VK_KHR_portability_enumeration (+ the portability
      instance flag) and VK_KHR_portability_subset when advertised, and gate
      robustBufferAccess2 on the device actually supporting it (Metal does
      not; the old code keyed it off robustImageAccess2 and vkCreateDevice
      failed with ErrorFeatureNotPresent).

    • Input: pad exports polled user32 GetAsyncKeyState, so POSIX hosts threw
      DllNotFoundException per scePadReadState call. The presenter now
      attaches the window's keyboard via Silk.NET.Input into HostWindowInput,
      and the pad exports map the existing VK-code layout onto it off
      Windows. Headless hosts (Linux containers) report a disconnected
      keyboard and fall back to neutral pad data silently.

    GLFW needs an x86-64 Vulkan loader under Rosetta: place a universal
    libMoltenVK.dylib next to SharpEmu named libvulkan.1.dylib (Homebrew's
    arm64-only copy cannot load into the x86-64 process) and export
    DYLD_LIBRARY_PATH to that directory.

    Verified: Dreaming Sarah boots to a MoltenVK-backed 2560x1440 window on
    macOS (Apple M4, Rosetta 2), renders the intro, title, and menus, and
    keyboard input drives it into gameplay. Linux (amd64 container) runs the
    same build headless through millions of imports with no faults. Windows
    paths unchanged; arm64 and x64 builds clean.

    • [posix] CoreAudio playback, self-contained MoltenVK loading, input/log polish
    • Audio: sceAudioOut ports now play through an AudioQueue backend on macOS
      (stereo PCM16 with the same 32KB backpressure pacing as the WinMM path).
      The WinMM port and the new CoreAudio port share an IHostAudioPort
      interface and sample converter; hosts without a backend (Linux
      containers) keep the silent fallback.

    • MoltenVK: GLFW resolves Vulkan with dlopen("libvulkan.1.dylib"), which
      cannot see the app-local universal MoltenVK build, so the presenter now
      feeds vkGetInstanceProcAddr straight into glfwInitVulkanLoader (GLFW
      3.4) before creating the window. No DYLD_LIBRARY_PATH needed; the CLI
      also preloads the dylib for Silk.NET and prints setup hints when it is
      missing. scripts/fetch-macos-moltenvk.sh stages the official universal
      dylib next to a build.

    • The virtual-range allocator's failure trace now names the address and
      length instead of "AllocateAt invocation threw".

    Investigated and documented (not port defects): the savedata transaction
    failure is identical on Linux and macOS (HLE argument-register mapping for
    sceSaveDataCreateTransactionResource), and the in-game tile speckling has
    no platform-specific code in its path - the one macOS-only delta is that
    MoltenVK lacks robustBufferAccess2, so out-of-bounds shader reads return
    garbage instead of zeros.

    Verified on macOS: window, audio backend, and keyboard input all come up
    with zero environment configuration; the game runs to gameplay. Linux
    headless run unchanged (silent audio, no faults). Windows paths untouched;
    arm64 and x64 builds clean.

    • [cpu] Preserve guest registers and flags across patched TLS accesses

    The TLS patch handler replaces guest mov reg, fs:[...] instructions,
    which preserve every other register and the flags - but the handler
    loaded the TLS index into ecx and called TlsGetValue (Win64: clobbers
    rcx/rdx/r8-r11) with sub/add rsp trashing the arithmetic flags. Guest
    code that keeps live values or comparison results across a TLS access
    computed garbage deterministically. The handler now saves rcx, rdx,
    r8-r11, and the flags around the call, keeping the same inner stack
    alignment. This applies to the load patches and both store-helper stubs,
    on every platform.

    Also in this change, from the rendering-artifact investigation:

    • The present blit picks linear filtering for any fractional scale
      (nearest only for integer upscales): a 3840x2160 guest frame blitted
      into a 2560x1440 swapchain with nearest silently dropped every third
      row/column.
    • ClampViewport no longer trims the guest viewport rectangle to the
      render target; trimming changed the guest's scale/offset and skewed
      texel addressing. Vulkan permits viewports beyond the framebuffer
      (the scissor confines rendering), so only spec bounds are enforced.
    • Env-gated diagnostics grown during the investigation: guest texture
      dumps (SHARPEMU_TEXTURE_DUMP_DIR), aliased guest-image readback dumps
      (SHARPEMU_TRACE_GUEST_IMAGES=alias), small-render-target write movies
      (SHARPEMU_TRACE_GUEST_WRITES=small), unattended input injection
      (SHARPEMU_AUTO_CROSS=secs,...), viewport nudging
      (SHARPEMU_VIEWPORT_EPSILON), chunked-draw toggle
      (SHARPEMU_DISABLE_CHUNKED_DRAWS), and rect-list/draw vertex traces.

    Known remaining issue (root cause narrowed, not yet fixed): the game's
    terrain texture pages are corrupted in guest memory before any GPU work

    • the mound's solid-fill 32x32 tiles decode to fully transparent texels
      and the grass page has deterministic gaps, byte-identical across runs.
      Ruled out: memcpy/memmove/memset/realloc HLE semantics, sampler wrap
      modes, texel-boundary rounding, chunked draws, viewport handling. Next
      step is auditing the Chowdren asset decode path (custom compressed
      images) against the emulator's import surface.
    • [linux] ALSA playback backend for sceAudioOut

    sceAudioOut ports on Linux now play through libasound instead of the
    silent fallback. The PCM device opens in blocking mode with ~170ms of
    device buffer (the time-equivalent of the 32KB queue the WinMM and
    CoreAudio ports keep), so snd_pcm_writei provides the same backpressure
    pacing without a managed queue. Underruns and suspend/resume go through
    snd_pcm_recover with one retry per submit; anything else drops the
    buffer rather than stalling the guest.

    The "default" device routes through PulseAudio/PipeWire on desktops
    and straight to hardware on bare ALSA; SHARPEMU_ALSA_DEVICE overrides
    it (the null device makes the path testable in containers). A missing
    libasound or device fails port creation and lands in the existing
    silent fallback.

    Verified in an amd64 container: the test game opens the port
    (backend=alsa, 48kHz stereo float32) and streams sceAudioOutOutput
    through the null device for a full run; without a usable device the
    port logs a warning and falls back to silent. Playback on real Linux
    audio hardware has not been tested.

    • [fixes] Address review feedback: commit bounds, CoreAudio shutdown, dump errors
    • HostMemory: a MEM_COMMIT that runs past its reservation now fails like
      Win32 instead of committing a prefix and reporting success. All current
      callers already clamp their ranges to the region, so this only guards
      future callers.

    • CoreAudioPort: Dispose wakes a submitter waiting on backpressure and
      the wait treats ObjectDisposedException as a timed-out wait, so closing
      a port during playback can no longer throw. A failed AudioQueueStart
      tears the queue down and fails fast instead of leaving an undrainable
      queue that stalls every later submit on its timeout.

    • AgcExports: texture dumping catches all write failures (bad path,
      permissions), logging a warning instead of crashing when
      SHARPEMU_TEXTURE_DUMP_DIR points somewhere unusable.

    Verified with the Linux container run: game boots and streams audio with
    the stricter commit check, and a dump dir under /proc produces warnings
    instead of taking the process down.

    • [ci] Build linux-x64 and osx-x64 archives

    Adds a build-posix matrix job (ubuntu-latest / macos-latest) mirroring
    the Windows build: locked restore, Release build, self-contained CLI
    publish, and a tar.gz artifact per RID (tar keeps the executable bit).
    The macOS archive also stages the universal MoltenVK dylib via
    scripts/fetch-macos-moltenvk.sh so the artifact runs without any manual
    Vulkan setup. The release job still only ships the Windows archive.

    • [cli] Keep POSIX glfw natives outside the single-file bundle

    The KeepGlfwOutsideSingleFile target only matched filenames starting
    with 'glfw', which covers Windows (glfw3.dll) but not libglfw.3.dylib /
    libglfw.so.3. Those got embedded into the single-file bundle, and
    Silk.NET's library loader does not probe the bundle extraction
    directory, so a published build died with "Couldn't find a suitable
    window platform" (and the glfwInitVulkanLoader wiring, which loads the
    library from AppContext.BaseDirectory, could not run either). Keeping
    the POSIX names loose next to the executable fixes both, the same way
    the Windows build already handled it.

    Found by running the CI-built osx-x64 archive: video failed while local
    loose-file builds worked. With the fix the published single-file build
    opens the MoltenVK window, wires the loader, and reaches gameplay.

    • [ci] Publish linux-x64 and osx-x64 release archives

    The build-posix artifacts now ship as per-RID GitHub releases on main
    pushes and manual dispatches, tagged the same way as the win64 ones
    (--). Archives stay tar.gz so the executable bit
    survives extraction.

    • [cli] Fail early on non-x86-64 host processes

    The CPU backend executes guest x86-64 code natively, so the process
    must be x86-64 (win-x64/linux-x64 on x64 hardware, osx-x64 under
    Rosetta 2 on Apple Silicon). An arm64 process previously failed deep
    inside emulation startup, indistinguishable from MoltenVK, signal
    handler, or guest memory problems. CLI mode now checks the process
    architecture up front and exits with a message naming the supported
    execution model (and the Rosetta install command on macOS). The
    GUI-only path stays usable on arm64.

    • [video] Log the selected Vulkan device name and API version

    The presenter never named the GPU it picked, so a 'no video' report
    could not be told apart from a real windowing failure without guessing.
    It now logs the device name, type, and API version right after
    selection. A software rasterizer (llvmpipe/lavapipe/SwiftShader) shows
    up here and typically lacks the device features the translated shaders
    need, which is the likely cause when a window opens and presents frames
    but nothing draws.

    • [video] Steer GLFW to XWayland on Wayland sessions

    GLFW's native Wayland backend does not reliably map the Vulkan window
    with some drivers (NVIDIA in particular): frames present but the window
    never becomes visible, so the game runs with audio and no picture. A
    report on an RTX 5080 showed exactly this — all device features present,
    frames presenting, but the log had 'libdecor-gtk.so failed to init' and
    a 1.4x-scaled window, both Wayland tells.

    On a Wayland session that also exposes an X server (DISPLAY set), the
    presenter now clears WAYLAND_DISPLAY for its own process before GLFW
    initializes, so GLFW selects its dependable X11/XWayland backend.
    SHARPEMU_ENABLE_WAYLAND=1 opts back into native Wayland. Headless
    (no DISPLAY) and non-Linux hosts are unaffected.

    • [video] Force GLFW X11 backend via the platform init hint, log the platform

    The previous attempt cleared WAYLAND_DISPLAY to steer GLFW off Wayland,
    but a reporter still hit the native-Wayland path (the Wayland-only
    libdecor error persisted), so that env trick doesn't switch GLFW.

    Use GLFW's supported mechanism instead: glfwInitHint(GLFW_PLATFORM,
    GLFW_PLATFORM_X11) before GLFW initializes, called into the same libglfw
    GLFW itself loads (the pattern InitializeMacVulkanLoader already uses).
    Still gated on a Wayland session with an X server present (DISPLAY set)
    so we never force X11 where XWayland can't catch it, and still
    overridable with SHARPEMU_ENABLE_WAYLAND=1.

    Also logs 'GLFW windowing platform in use: ' after init via
    glfwGetPlatform, so a 'no window' report shows X11 vs Wayland outright.
    Verified on macOS: the readback correctly reports Cocoa and the
    presenter is unaffected (the fix is a no-op off Linux).

    • [video] Run the GLFW window on the main thread on Linux too

    GLFW requires window creation and event processing on the process main
    thread on every platform: initialization, window creation, and
    glfwPollEvents are main-thread-only, and X11 in particular has a single
    event queue that must be serviced there. A window created and polled on
    another thread may never map — which is why the game ran (audio, imports,
    even Vulkan present) with no visible window on Linux.

    macOS already routed the window loop to the main-thread pump (AppKit
    needs it); Windows is fine because it has a per-thread event queue. Linux
    was the gap: it spawned a background thread for the presenter. Extend the
    existing HostMainThread pattern to Linux — emulation runs on a worker,
    the main thread pumps the window work the presenter posts.

    Refs GLFW intro guide (thread-safety): init, window creation, and event
    processing are restricted to the main thread.

    Verified: macOS still boots to its window unchanged; the Linux headless
    container runs to millions of imports with no deadlock or regression.
    On-screen confirmation on a real Linux desktop is still pending, but this
    is the documented root cause for a windowless-but-running Linux session.

    • [posix] Skip Win32 native guest workers

    • [vulkan] Synchronize offscreen targets before present

    • [vulkan] Transition fresh textures from undefined layout

    • [vulkan] Report swapchain pixels before source readback

    • [vulkan] Emit requested guest image diagnostics

    • [agc] Diagnose guest texture fallbacks

    • [linux] Keep guest GPU mappings in low address space

    • [video] Reduce diagnostic stalls and drain complete frames

    • [memory] Harden packed GPU address handling

    • [readme] Document Linux and macOS release support

    • [posix] Integrate the host platform abstraction

    • [posix] Restore guest thread address window

    • [video] Run the performance HUD on POSIX hosts

    The FPS/CPU/work HUD bailed out unless the host was Windows; only the
    per-thread hottest-thread scan actually needs Windows APIs. Keep that
    scan Windows-only (POSIX reports 'idle') and let the rest of the HUD
    run everywhere — the title is already set from the render thread, which
    owns the window on macOS and Linux.

    • [posix] Implement native guest worker threads

    Guest entry stubs must not run above CLR-managed frames on CLR-created
    threads (see the NativeWorker preamble); the PR previously fell back to
    the inline calli path on POSIX, which reproduced the documented
    'attempted to call a UnmanagedCallersOnly method from managed code'
    fail-fast (observed after Dreaming Sarah's menu select) and left the
    runtime's suspension machinery walking guest frames.

    Provide the missing POSIX half of the worker loop:

    • PosixHostStubs grows Win64-convention WaitForSingleObject/SetEvent/
      ExitThread stubs backed by dispatch semaphores (macOS) / unnamed POSIX
      semaphores (Linux) plus pthread_exit, with EINTR retry in the wait.
    • Worker events are creatable/signalable/waitable from managed code too,
      so NativeGuestExecutor.Run keeps its handshake (AutoResetEvent stays
      on Windows byte-for-byte).
    • PosixHostThreading implements CreateNativeThread/WaitForThreadExit/
      CloseThreadHandle over pthreads (liveness probed with
      pthread_kill(0), then joined).
    • RunPrologue/RunEpilogue are routed through the existing Win64->SysV
      thunks, so the emitted loop stays identical across platforms.
    • [macos] Disable concurrent GC under Rosetta's write-watch hazard

    Background GC's write-watch revisit (SoftwareWriteWatch::GetDirty ->
    FlushProcessWriteBuffers) calls thread_get_register_pointer_values on
    every thread; under Rosetta 2 that Mach call stalls indefinitely on
    threads executing translated guest code. The background mark phase then
    never finishes and every allocating or Monitor-taking thread wedges
    behind it — observed as Dreaming Sarah freezing at the menu/loading
    screen with FPS 0 in 5 of 7 runs, dispatcher/watchdog parked in
    Monitor.Enter and all BGC threads waiting in t_join.

    Non-concurrent GC never takes that path; a 5-minute soak now holds
    22-31 fps in-game with zero stalls. Windows and Linux keep concurrent
    GC.

    • [diag] Periodic guest-thread snapshots with gate-owner tracking

    SHARPEMU_PERIODIC_SNAPSHOT_SECONDS=N dumps the stall snapshot every N
    seconds even while imports are progressing, for soft stalls where the
    game stops advancing but threads keep spinning. The periodic dump never
    touches the guest-thread gate (it must keep reporting when the gate is
    what's wedged): it reads a lock-free owner record — every gate
    acquisition now goes through LockGate(site), which notes site/thread —
    and walks the thread table without the lock, tolerating torn reads.
    SHARPEMU_PERIODIC_SNAPSHOT_FILE redirects the dump to a side file for
    the case where the console itself is wedged (frozen log mirror was one
    of the observed failure modes).

    • [nuget] Add osx-x64 RID targets to lock files

    • [cpu] Back off the guest join poll

    TryJoinThread polled the host thread at a fixed 1ms; a game main thread
    joining a long-lived worker (Dreaming Sarah parks there for the whole
    session) burned ~5% of managed CPU in Join/Sleep syscalls. Ramp the
    poll interval to 10ms once the join is clearly long-lived — exit
    detection latency for long joins moves from ~1ms to at most 10ms, and
    short-lived joins still resolve on the first 1ms polls.

    • [nuget] Add linux-x64/win-x64 RID targets to lock files

    • [posix] Keep guest stacks clear of the import-stub descent

    The import-stub region descends from 0x7000_0000_0000 on the same 16MB
    grid as the guest thread windows; moving stacks to 0x6FFF_E000_0000 put
    them inside the stub region's 64-module descent range (floor
    0x6FFF_C000_0000), silently consuming the top ~32 stack slots on hosts
    with many loaded modules. Drop the POSIX stack base to 0x6FFF_A000_0000:
    512MB below the stub floor, still 2.5GB above the TLS window. Windows
    keeps 0x7FFF_E000_0000 (its bands are ~15TB apart).

    • [pad] Read window gamepads on POSIX hosts

    XInput and the DualSense hid reader are Windows-only, which left
    macOS/Linux with keyboard input only. The presenter's Silk/GLFW input
    context already enumerates gamepads on both platforms, so track their
    state in HostWindowInput (event-driven on the window thread, snapshot
    guarded like the key set) translated to ORBIS conventions: GLFW's Xbox
    layout maps A/B/X/Y to Cross/Circle/Square/Triangle, sticks bias from
    -1..1 to 0..255 with Y growing down, and triggers rescale from GLFW's
    -1..1 resting-at--1 range with digital L2/R2 bits past 25%.

    The merge into ReadHostInputState is gated to non-Windows so a physical
    pad is never sampled twice through both a native reader and GLFW.
    Hotplug is handled via ConnectionChanged; with no pad connected the
    path is inert.

    Untested against a physical controller (none attached to the dev host);
    axis conventions follow the GLFW gamepad-mapping contract.

    • [nuget] Refresh lock files after cross-RID restores

    • [posix] Adopt the host audio/input seams from main

    Main's #192 abstracted audio output and pad/keyboard input behind
    IHostAudioOutput/IHostInput; re-express the POSIX backends behind them:

    • CoreAudioPort/AlsaAudioPort move to Host/Posix as
      PosixCoreAudioStream/PosixAlsaAudioStream implementing
      IHostAudioStream. The seam converts to stereo PCM16 before Submit, so
      the ports' own conversion (and IHostAudioPort/AudioSampleConverter)
      is gone; queueing and backpressure are unchanged.
    • PosixHostAudio selects CoreAudio (macOS) / ALSA (Linux) as the
      platform's IHostAudioOutput.
    • PosixHostInput implements IHostInput over an
      IPosixWindowInputSource that HostWindowInput registers when the
      presenter attaches the window's GLFW input context: keyboard with
      virtual-key translation, the window gamepad snapshot (now in seam
      HostGamepadState/HostGamepadButtons terms), and keyboard-connected as
      the focus signal. Rumble/lightbar no-op (GLFW has no such API).
    • PadExports drops its direct HostWindowInput gamepad merge — pads now
      flow through IHostInput.GetGamepadStates like every platform.
    • PosixHostThreading.RequestTimerResolution is a documented no-op.

    All three RIDs build; SharpEmu.Libs.Tests pass (26/26).

    • [nuget] Regenerate GUI lock file for RID-less locked restore

    Local cross-RID builds stamped a win-x64 runtimes section into
    SharpEmu.GUI's lock file; the project declares no RuntimeIdentifiers,
    so CI's 'dotnet restore --locked-mode' failed with NU1004 on every
    platform. Regenerated via a plain solution restore (--force-evaluate),
    matching what the workflow validates.

    Downloads