On Windows every direct host import leaves the write watch, and a storage
image over it compares its whole surface with its guest-byte copy at every
collect epoch. #4432 measured 7-8.6 s of every 10 s in those compares on a
GTA V scene, with 98% of the compared surfaces unchanged.
Tracker blocks that leave the watch now keep their write-watch marks as a
hint. Leaving the watch gives a block a fresh hint stamp, and
UntrackedHint walks a range of such blocks, at most once per collect epoch
and thread, stamping the blocks whose pages a CPU store marked; it
returns the newest stamp of the range. compareUntracked puts that stamp in
its memo key in place of the collect epoch, so an image is compared again
after a mark in its range, a driver store, a mapping change or a recorded
GPU write. The byte compare still decides whether anything changed: a mark
the driver makes without a CPU store costs one compare and drops nothing.
Nothing else reads the hint. Collects, Watched, UnchangedSince,
ChangedBlocks and NewestStamp still treat those blocks as untracked, and
Linux is unchanged.
agc_driver_dcc_keys_first_write refreshes an imported storage image over
50 collect epochs with no store: it compared the image 50 times before and
0 times now. A CPU store is still uploaded on the next refresh, and a store
of the bytes already there costs one compare and keeps the pending
results. agc_write_tracking checks the hint itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build SDL's Wayland backend next to X11 and ask SDL for "wayland,x11"
on Linux, so the window runs natively under a Wayland compositor instead
of through XWayland. SDL loads libwayland, xkbcommon and libdecor at run
time, so systems without them, or sessions without WAYLAND_DISPLAY, keep
using X11. SDL_VIDEODRIVER still overrides the choice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shadPS4 looks the request up through its library and user contexts and
returns the matching NOT_FOUND error. This library keeps no request
table: CreateRequest hands out counter values and the other request calls
accept any id, so the checks wait for request tracking across all of
them. The entry moves to Silent stubs, as main's conventions check now
requires.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The timeout setter takes (request id, timeout) and succeeds without
checking the request, like the other request setters: no request reaches
a service, so no timeout can expire. PPSA14632 imports it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A shared range larger than one chunk now takes the sparse path only when
the device binds sparse memory, udmabuf is open, the range's backing
offset is on the sparse block size, the sparse buffer accepts every
memory type a chunk import can have and the backing file covers the
last chunk. All of that is known before a chunk is imported. Once the
path is chosen, a failed chunk import, fence creation, bind, fence wait or device
address throws instead of going on to the whole-range udmabuf and copies.
agc_host_import_sparse checks the import against a mocked device with
the real udmabuf: a range above size_limit_mb becomes one sparse buffer
bound to one chunk import per 64 MiB, an unaligned backing offset
imports no chunk, and failures of UDMABUF_CREATE and vkQueueBindSparse
after the decision throw.
Charge each recorded protection ranges 2 MiB granules to the gpu
pool when the range has GPU-access protection bits and to the cpu
pool otherwise, so the available counts reflect committed usage
instead of staying at the 2048-entry budget. Follows KytyPS5s
counting model.
A draw made outside the GPU lock (agc_driver_recorded_kept_bytes) imports a target's memory and leaves the watch there; the hook threw instead of saving. It now takes the lock, as FlushPending does.
radeonsi exports MRT0 as 32_AR when alpha-to-coverage is on and no color
buffer is bound, so a depth-only draw can depend on that alpha. The
decoder now accepts 32_AR and an absent MRT0 target, and reads the MRT0
export format from SPI_SHADER_COL_FORMAT, which it masked to 0 for
draws without a color target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pixel export to an MRT whose SPI_SHADER_COL_FORMAT is 32_AR read its
alpha from export channel 3 (EN mask 0x9), the layout before GFX10. On
GFX10, which the PS5's gfx1013 is, the alpha is channel 1: Mesa's
emit_ps_color_export (ac_nir_lower_ps_late.c, index
gfx_level >= GFX10 ? 1 : 3), ACO and radeonsi in Mesa 22.0
(enabled_channels 0x3 with the alpha in channel 1) and LLPC's
FragmentColorExport.cpp (EXP_FORMAT_32_AR, exports[1]) all emit it
there. A console shader's `exp mrt0 v0, v1, off, off` therefore lost
its alpha: it stayed at the 1.0 default, so an alpha-only COLOR_32
ALT_REV target stored 1.0 and an R32 target blended with SRC_ALPHA
blended as if the alpha were 1. With channel 3 enabled, channel 3 was
taken instead.
ExportVector now reads channels 0 and 1 for 32_AR and puts channel 1
in the alpha component; channels 2 and 3 are ignored. The layout comes
from the compilers, not from a hardware measurement, and is recorded in
docs/dev/debt/shader.md.
agc_driver_pixel32_ar_export draws 32_AR exports with EN 0x3, 0xb and
0xf, as wave32 and wave64, into a COLOR_32 FLOAT ALT_REV target and a
COLOR_32 FLOAT target blended with SRC_ALPHA and ONE_MINUS_SRC_ALPHA,
and compares every stored value bit for bit. Without the fix every row
fails: the stored value uses an alpha of 1.0 or of channel 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux a shared-backed guest range is imported through one udmabuf
covering the whole range, and udmabuf refuses buffers above its
size_limit_mb (64 MiB by default). Larger ranges fell back to copies; an
address-based dispatch then synced its whole mirror, storing back every
pending storage image inside it each time.
Such a range is now one sparse buffer bound to the existing 64 MiB chunk
imports. The bind runs on a sparse binding queue of its own when the
device has one, so imports made at map time (without the GPU mutex) can
use it; otherwise only under the GPU mutex on the main queue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, singly mapped direct memory is private since #5596, so its host
import is a direct import that leaves the write watch instead of a shared
alias that stays watched. A storage image whose results were pending when
its memory was imported had no saved guest bytes, and storing them threw.
GuestMemory now calls a hook before Unwatch and before an ImportWatched
that does not stay watched. Every image with results pending over the
range saves the guest bytes of its pending units that have none, then
collects and records the pieces the keep rule leaves to the CPU for the
writes the watch saw. The next store and Refresh leave those pieces to the
CPU and compare the rest with the saved bytes. A map-time import made
without the GPU lock is deferred to the locked retry when pending results
overlap the range.
Writes the 80-byte status described in #2746: the sceVideoOutGetOutputStatus
fields followed by zeros, with the documented codes for a null status and an
unknown handle.
(cherry picked from commit e893efad2d0a7b90243d07bf6669cc9b5f540432)
fstat on a guest socket descriptor passed the guest number to the host,
which has no such descriptor, and threw ("FillFileStat: fstat failed for
fd 268435460"); sceKernelFstat returned EBADF. FreeBSD fills the stat
buffer for a socket through soo_stat (sys/kern/sys_socket.c):
bzero((caddr_t)ub, sizeof (*ub));
ub->st_mode = S_IFSOCK;
...
if ((sb->sb_state & SBS_CANTRCVMORE) == 0 || sbavail(sb))
ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
ub->st_size = sbavail(sb) - sb->sb_ctl;
...
if ((sb->sb_state & SBS_CANTSENDMORE) == 0)
ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
and pru_sense_null (sys/kern/uipc_socket.c) adds
st_blksize = so->so_snd.sb_hiwat.
fstat and sceKernelFstat now answer a guest socket from the socket
table: S_IFSOCK, the read bits until the receive side is shut down and
drained, the write bits until the send side is shut down, st_size from
the bytes queued and st_blksize from the send buffer size, other fields
zero. shutdown records which side it closed. A closed socket descriptor
returns EBADF. The differences from the console (peer shutdown, the
host's FIONREAD and SO_SNDBUF, socketpair identity) are listed in
TechnicalDebt.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hull programs whose relative patch index (v1 bits 7:0) reached anything
but a tessellation ring or LDS address threw, because the lowering runs
every Vulkan patch as relative patch 0. The quad terrain hull 0x80002fc700
in Ghost of Yotei is one, and so is the domain 0x8068fbd900 (v7).
Such hulls and domains now take (PrimitiveID mod GE_CNTL.PRIM_GRP_SIZE)
mod VGT_LS_HS_CONFIG.NUM_PATCHES, as if hull threadgroups took NUM_PATCHES
patches at a time out of each primitive group; both stages see the same
PrimitiveID for a patch. This holds only when every instance starts a new
threadgroup, so it needs GE_CNTL.BREAK_WAVE_AT_EOI (bit 18); with the bit
clear the instances share threadgroups and such programs still throw.
PRIM_GRP_SIZE and BREAK_WAVE_AT_EOI join the tessellation configuration
(draw keys, hull and domain cache keys, request version 24). A missing
GE_CNTL throws, and so do local programs, whose relative vertex index
depends on vertex reuse that Vulkan does not expose.
sceNetConnect() on a connected datagram socket rejected an address
whose family is AF_UNSPEC (a null address) with EINVAL before touching
the socket, so the socket stayed connected: sceNetGetpeername() still
reported the old peer and datagrams from any other sender were dropped
by the host.
FreeBSD 11.4 disconnects a connected datagram socket first and only then
looks at the address. sys/kern/uipc_socket.c, soconnectat():
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first. This allows
* user to disconnect by connecting to, e.g., a null address.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
(error = sodisconnect(so)))) {
udp_disconnect() (sys/netinet/udp_usrreq.c) clears the peer and keeps
the local port. connect(2): "Datagram sockets may dissolve the
association by connecting to an invalid address, such as a null
address." libkernel's connect gets the same change in #4498.
A connected datagram socket given an address whose family byte is 0 is
now disconnected on the host first (AF_UNSPEC on Linux, a zeroed
address of the socket's family on Windows), and the call then fails
with EINVAL as before. Linux also drops an ephemeral local port on that
disconnect, so the port is bound again on the wildcard address, which
is what FreeBSD leaves. A host failure during the disconnect is
returned as the call's error. Stream sockets keep the previous path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>