The smart 2D filtering check and the exact one for depal walked the rectangles separately. Now one loop
does both, stopping once neither can hold, and a rect of no size ends both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shader depal filters by hand, with four texel and four palette lookups per pixel. DBZ Tenkaichi Tag Team
does two full-screen through-mode depal passes per frame, and on a PowerVR GE8320 (Galaxy A21) they took
DBZ from 8.3 to 14.4 seconds per 10 emulated seconds once #22204 had made framebuffer depal work again.
They're 1:1, where bilinear gives the nearest texel anyway, so rectangles that put every pixel center on
a texel center (equal UV and position deltas, at a whole-texel offset) now count as pixel mapped for
depal, which turns the filter off. Unlike the smart 2D filtering check, this changes nothing on screen:
the frame dumps render identically on Vulkan and OpenGL, 69 of them through this path. DBZ is down to 9.2s.
The depal uniform is also repacked when pixelMapped changes between draws, rather than only when the
texture is reapplied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every instance after the first got PPSSPP_ID 1 on macOS, so they shared
the same ad hoc MAC and IP, and secondary instances saved the config.
- flock() on a shm object fails with ENOTSUP on Darwin, so the counter was
never updated. Lock with a named semaphore there; keep flock elsewhere.
- ftruncate() on a shm object that already has a size fails with EINVAL on
Darwin. Only size the object when it's new.
- Shutdown unlinked the shm object even while other instances were still
running, so the next one to start got a fresh counter and ID 1 again.
Only unlink when the last instance exits.
The loader streams it (HTTPFileLoader), but headless rejected anything File::Exists doesn't
find. This lets a phone run a game served from another machine, over adb reverse or the LAN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP never writes the info struct's length, and on a call without new data writes updated = 0 and
the last data's timestamp again, 0 before any came (pspautotests net/adhoc/pair/gamemodeinfo).
PPSSPP wrote the length and left the timestamp alone without new data. Shaun White Snowboarding
reads the timestamp either way, from a struct on its stack, and keeps the newest it has seen as the
time of the last update; stack garbage got in there, and the host gave up on the game a moment
after the match started ("The game is no longer available").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, with 8192 bytes in flight from an 8 KB send buffer, poll leaves out SEND and a
non-blocking send of 16 more returns WOULD_BLOCK until about half the buffer has drained, and poll
leaves out FLUSH until the data has arrived, which takes as long as a flush (pspautotests
net/adhoc/pair/ptpsendready). PPSSPP reported both at once and took any send. The in-flight bytes
from the flush model now drain linearly until their delivery time; sends that don't fit wait, and
GetPtpStat counts what's left. Relay PTP sockets now record their buffer size, which they didn't.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait polled the emulated clock with a new connection every few milliseconds, thousands of them
in a long wait, each with its own thread and subscribers on the PPSSPP side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Feed() held pendingLock_ across Core_RunOnCPUThread(). Once sendFeed_ was set, the CPU thread's
next flip waited for that lock in FlipListener(), while Feed() waited for the CPU thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DisplayFireFlip() copied the listener list and called the copy without the lock, so a debugger
connection closing on its own thread could free its GPU stats subscriber while the CPU thread was
about to call it. Headless crashed (or hung) in WebSocketGPUStatsState::FlipListener() with
scripts that open a connection per command. Hold the lock across the calls, as LogManager does for
its callbacks, so __DisplayForgetFlip() returns only once the listener can't be running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP returns size 0 without touching a buffer too small for one entry, and GetPeerList refuses
a negative size with INVALID_ARG (pspautotests net/adhoc/pair/listsizes). PPSSPP cleared the
whole buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a PSP does, where PPSSPP differed:
- A PtpOpen or PtpListen on a port in use uses up a socket ID, as PdpCreate's does.
- A connect to a MAC that isn't in the group would block or times out, and is refused once the
retransmits would have run out, (count + 1) * interval. It returned INVALID_ADDR at once.
- PtpAccept's flag must be 0 or 1.
- A blocking broadcast returns 0, so one the host can't take in time is dropped, not a TIMEOUT.
The FIXMEs and TODOs other tests already answer (socketids, adhocctl, gamemode, ptp) now say what
the PSP does instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pspautotests net/inet/calls measures what socket, bind, listen, connect,
accept, the socket options, getsockname and getpeername, shutdown, close
and the address conversions cost on a PSP when they don't wait: from 3
us for an address conversion to 76 us to close a TCP connection (95 us
with RST), and 4 us on a socket that isn't open. They cost about 1 us
each here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, select and poll with nothing ready return about 120 us after
their timeout (pspautotests net/inet/timing), wait at least about 330 us
for any timeout from 10 us up, and don't wait at all for a timeout of
1 us. We waited exactly the timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP a non-blocking flush right after a send returns WOULD_BLOCK, and 0 once the data is in
the peer's buffer, about 4.5 ms plus 2.4 us per byte, and GetPtpStat counts the bytes as unsent
until then (pspautotests net/adhoc/pair/ptpflush). PPSSPP's flush returned 0 at once. NBA 2K13's
host sends the match info, flushes until it gets 0 and closes; with the instant flush it closed in
the same frame, and the joiner, which checks for a disconnect before reading, never read the last
chunk ("There was a problem transferring the match information").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetScanInfo returned nothing while in a group. On a PSP the list from the last scan stays after
Create, Join and Disconnect, and only a scan attempted while connected empties it (pspautotests
net/adhoc/pair/scaninfo, added here along with ptpflush). NBA 2K13's joiner looks for the group it
just joined in the list and leaves when it isn't there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP refuses it immediately (pspautotests net/adhoc/ptp, "Connecting to nobody"), while the
relay held it for about 4 seconds before giving up. NBA 2K13's joiner connects to each entry of
its peer table in turn, itself first, and gave up on the group before it got to the host.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuilding the stencil after a reinterpret (89fabe21fb) copied the whole
destination to a temp buffer and redrew the stencil from it in a pass of its
own. Tantalus games reinterpret about 70 times a frame, and on an M1 with
Vulkan that made MX vs ATV Untamed 44% slower (GPU bound).
Now a stencil variant of the reinterpret shader reads the source the same way
the color reinterpret does and draws the stencil from its alpha, into the same
rectangle right after it, so it lands in the same render pass and needs no
copy. One draw with stencil export, otherwise a clear and one per alpha bit.
The 1141 dumps in ppsspp-framedumps render identically before and after, on
Vulkan (export) and OpenGL (per bit) on macOS. Split Second, OutRun and
Ultimate Ghosts 'n Goblins dumps go through this path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ninja only knew git-version.cpp from its timestamp at the start of the
build, so when the GitVersion step rewrote it, the object wasn't rebuilt
until some later build happened to notice. An Android APK built right
after a commit could carry the previous commit's version string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Emulation does a burst of work each frame and then waits, so the scheduler
sees a mostly idle thread and moves it to slow cores at low clocks. On
Android 13+, report the frame thread's CPU time per frame against a target
of half a frame. CPU time leaves out the frame limiter's sleep and any
blocking on vsync or the GPU. Idea from Azahar.
Does nothing on devices without hint session support.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setFramesPerDataCallback forced callbacks of PROPERTY_OUTPUT_FRAMES_PER_BUFFER
frames, which made Oboe add a block adapter holding back an extra buffer
whenever that doesn't match the burst. On a Galaxy S21 Ultra the MMAP stream
has a 96 frame burst against a 192 frame property. NativeMix takes any size.
The buffer was fixed at two bursts, 4 ms there, with nothing to recover from
underruns. Use Oboe's LatencyTuner, which starts at two bursts and adds one
per underrun, and log the underrun count when the stream closes.
Also take the lock when clearing the error and info strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android batches touch moves and joystick axis events until the next UI
vsync, up to a frame of latency. On API 30+, ask for them unbatched.
Non-pointer sources only apply to the focused view, so the surface view
now stays focused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A press and release that both land between two sceCtrl samples were
lost. Now the next sample sees the button pressed. Idea from Azahar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- various bug fixes
- allow disabling enet when creating relay and adhocctl server objects
- pdp_peek_next_size can now be used to check whether a pdp session has no packet or an upcoming 0 size packet
The game runs without its disc there, so a state saved from it would keep that broken
environment. Saves are refused, and so is --save-state together with a state to boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The count is opens of files missing from the disc, and reads and seeks on handles whose file
was dropped as the state loaded, from the load on. It tells how far a game booted without its
disc has strayed from its usual path. headless_bench.py takes bare .ppst files and directories
of them (--state-dir), and reports the count with the timings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Running a .ppst boots a placeholder program, with the disc ID and boot type peeked from the
state, then loads the state over it. An ISO game gets a disc with nothing on it, which the
mounts now load into by name. Meant for measuring performance with states that can be kept
without the games; the app still refuses to boot a state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>