mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-13 16:31:29 -10:00
fd17192acb66cffdf60960f152fa3dad0e0ca95c
145
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0663a163c2 |
scan: Refresh All scans a shared folder once per group
Refresh All walked and checksummed a mixed folder once for every playlist a Fully Automatic scan of it had filled. It now reads every playlist's settings before it runs one, keeping only its name, folder and settings, and loads each playlist again when it runs, so it still holds one playlist at a time. The load turns the saved settings into text, so grouping compares strings and stats no folder on the main thread. Playlists named after an installed database, matched strictly against it alone with no DAT file, saved with no other database name and otherwise equal settings, overrides applied, share one group scan. The group runs at its first playlist's place under one line and task title naming the folder and the playlists, and the summary still counts playlists. Any other playlist, and every --scan-refresh, runs alone as before. |
||
|
|
5756db8659 |
Input: Plays As, several controllers on one player, with hotkeys of their own (#19916)
* OpenXR: the controllers' slots are not read from the driver's view
A joypad driver that keeps its pads' state lends it to the snapshot
bridge through view(), and the bridge reads that in place of a copy
(
|
||
|
|
e98e1da005 |
vfs smb, scan: read every smb:// URL from the configured server
The VFS reads the configured server whatever host a URL names, so a scan or refresh no longer compares the host: a playlist saved under an older server address refreshes after the SMB server setting changes, and the check takes no name lookup. A share other than the configured one is still refused. vfs_test checks URLs of other hosts, unresolvable ones included, pass on their share alone. |
||
|
|
6ef3af70f8 |
scan: say why an smb:// folder failed; refuse another host or share
A scan or refresh of an smb:// folder that could not be read said "not found" or "skipped: content folder not found", whatever the cause, and logged nothing; in the menu, a scan scanned the folder path as a single file. A URL naming another host or share than the SMB settings was read from the configured server and share all the same. The scan task, the --scan path task and the playlist load of a refresh now check such a folder on the task worker before the scan starts: no SMB server set, a URL host or share other than the settings', or, when the folder cannot be read, the share's recorded connect failure (name lookup, connect, login, share). A URL refused for its host or share is never stat'd by them. The reason goes to the menu notification, one log line and the --scan line or the refresh's skip line. The item still fails, or the playlist is still skipped and left untouched, as before. A folder the server reports missing keeps the message it had. The man page states the host and share rule. |
||
|
|
08e5b72a88 |
Scan from the command line: exit status, Manual Scan options, playlist refresh (#19977)
* file_path: stop at the end of the path after '~'
fill_pathname_expand_special() skipped two characters after a leading
'~' or ':', so a bare "~" read past its NUL and "~x" lost the x. It
now skips the prefix and a slash after it, if there is one.
* scan: hand the callback user data and what the scan did
task_push_dbscan() and task_push_manual_content_scan() take user data
for their callback, and the callback's task_data is now a
task_dbscan_result_t: how many entries the scan added, how many its
playlists already held, and how many files matched nothing. A file is
counted by what became of it, so an archive that is expanded is not
counted as unmatched, and a loose scan's unmatched files count as
added. Callers pass NULL for now.
* scan: say in the result when a scan failed
A scan that ends before it runs (bad setup, no valid content, a DAT
that will not load) or ends with a task error now sets the result's
failed flag. A database folder that is missing or unset is treated as
an empty one, so it reports the same "no database" error instead of
finishing as if it had scanned.
* scan: count the files a scan could not read
A file whose scan fails (an unreadable file, a failed lookup) was in
no count. The result now carries an errors count for them. They do
not fail the scan.
* scan: print --scan errors on stderr and totals on stdout
A command-line scan now ends with a totals line on stdout, or with the
reason it failed on stderr. A file it could not read is listed on
stderr. The ++/==/?? marks are coloured only when their stream is a
terminal, so pipes and redirected output get plain text; on Windows
redirected output keeps its marks instead of losing them to
WriteConsole.
* scan: the manual scan finder skips a finished task
A finished scan stays in the task queue while its callback runs, and
the finder that refuses a second scan for the same playlist still
matched it, so a scan pushed from that callback was refused.
* retroarch: run every --scan, then exit with what the scans did
Each --scan is collected while the command line is parsed and run in
turn once it is done, so later options and further --scan flags are
no longer dropped. A path is expanded and made absolute first; one
that does not exist, or a scan that cannot start, is reported on
stderr and the next path still runs. --scan with --cloudsync or
--command is refused.
Without --menu the exit status is 1 if any scan failed, else 0 if any
added an entry, else 2, and a content path given with the scans is
ignored, with a line on stderr. With --menu each finished scan starts
the next, as a second scan is refused while one runs.
* manual_content_scan: clear the database name a refresh did not save
A playlist with no saved database name left the name buffer
uninitialised, and the refresh compared it against every installed
database, so whatever was on the stack could pick a database the
playlist never named.
* scan: Skip Database References names the scan's one playlist
The option was meant to put the playlist's name in each entry's
db_name, even for a database match (#18641), for users who build
their own thumbnail sets. When the scan picks the playlists, db_name
also picks the playlist an entry goes to, so with the option on every
match went to one playlist named after the content folder.
It now applies only when the scan writes one playlist, and gives
that playlist's name, a loose scan's unmatched entries included. With
the target on <Automatic>, or Fully Automatic with the option left on
from a Custom scan, matches go to their databases' playlists as with
it off; the entries of a custom playlist look their thumbnails up
under its name, not the folder's.
* scan: name the scanned folder in the start line
The "Scanning content" line, in the log and in --scan's output, has
named the database folder since the manual scan and the database scan
were merged (
|
||
|
|
6ebf66b643 |
OpenXR: per-screen placement for headset screens, and Edit Screens to arrange them by hand (#19945)
* video: per-screen geometry for the headset's floating screens Each floating screen, by the core's logical screen id, gets its own offsets, yaw, pitch, roll, scale and visibility (video_screen_xr_t), applied on top of where the layout puts it. video_views_xr_reset gives the defaults and video_views_xr_normalize clamps each value to its range: offsets to 10 m either way, angles to 180 degrees, scale from 0.1 to 10. A planner given no screens uses the defaults, so nothing moves until a setting does. * configuration: save per-screen headset geometry video_screen<N>_xr_x, _y, _z, _yaw, _pitch, _roll, _scale and _visible keep each floating screen's adjustments for screen ids 1 to 8, and survive configuration overrides like the other video settings. A minimal save writes only the values that differ from the defaults. The video_screens settings sample checks the round trip and the override cases, and runs in the OpenXR input and Wine views jobs. The defaults fixture now links video_views.c, which the reset uses. * menu: per-screen controls for the headset's floating screens Stereo 3D & Screens gains Screen, which picks a floating screen by its logical id among those in the current frame's map, and for it Horizontal, Vertical and Depth Offset, Yaw, Pitch, Roll, Scale and Visible, with Reset Screen and Reset All Screens. The rows apply live and are hidden where they cannot apply, such as a tracked projection's eyes. Stepping between screens reads one snapshot of the map and is bounded, so a tracking change mid-step cannot leave it walking an empty map. docs/openxr.md describes the controls; the video_screens sample drives them through the shipping menu actions. * video: apply per-screen headset geometry to the frame snapshots Each XR planner, Vulkan's and GL's, takes the normalized screen settings with the frame snapshot, so a screen moves, turns, scales or hides as its rows change, while paused too; recentering and tracked projections are unaffected. Screen images are kept between frames, so shader feedback survives a placement change. A retained XR target that cannot be allocated is skipped quietly, remembered by its size so it is not retried every frame; only the desktop UI and canvas targets log the failure. Covered by openxr_core_test, the video_screens sample, the e2e runner's feedback shader case and the new vulkan_views_target sample. * android: keep the OpenXR EGL context C89 gl_xr_egl_init declared its extension string after statements. * video: pose inverse, layout spots and grab maths for headset edit mode Each item's layout spot comes from one helper that placement uses and the quad set records per slot, so a shown pose turns back into the settings that placed it. Grab maths: the item follows the aim, level unless tilting, offsets clamped to the settings' range; the stick pushes along the ray and resizes. Edit picking sees every item and takes the menu by a handle bar under it. * video: a frame without a view map is screen 1 in the headset A core without a views map (SNES, say) had no screen to select, so its frame could not be moved. Its whole frame now takes screen 1's offsets, turn, scale and visibility, and the selector lists Screen 1. gl's window stays as laid out while the menu is open over it. * video: the headset's menu in front of screen 1, or where it was put The menu sat at a fixed spot, so moving screen 1 closer left the menu behind it. It now floats 10 cm in front of screen 1, facing as it does, at its default angular size and never nearer than 0.4 m, while screen 1 floats and shows, and is still submitted after the screens. A menu place (video_openxr_menu_xr_* with video_openxr_menu_placed) overrides that; it is saved like the per-screen keys, and Reset All Screens clears it. * input: Edit Headset Screens mode for the OpenXR controllers Edit Screens starts from a hotkey, a headset action, the HEADSET_EDIT_SCREENS command or a Stereo 3D & Screens row, and stops on the same, on opening the menu, on a video reinit or when the session ends. It syncs its own action set in place of the game's, points the lasers at every item whatever the Laser Pointer setting, keeps the menu's quad up while editing, and with While Editing Screens set to Pause pauses only content that was running. A runtime that refuses its bindings keeps the game's and leaves edit mode unavailable. The setting reaches the headset's poll with the others, and the pause and resume are asked of the frontend as platform requests, so the input driver issues no command and takes no settings itself. A Laser Pointer value past its choices, which a hand-edited config can hold, reads as Auto rather than as edit mode's. * input: grab, tilt, push and resize headset screens in edit mode A grip on an item takes it in the screens' frame: it keeps its pose relative to the hand, stays level unless the trigger is held, and the stick pushes it along the laser and resizes it, past a deadzone. Each poll writes the item's offsets, turn and scale where the menu's rows write them, through named frontend calls, clamped so it waits at a limit and resumes from there. The menu is taken by its handle; a menu that follows screen 1 is placed as large as it shows. The main button resets a free item as Reset Screen does, or puts the menu back in front of screen 1. A recenter, a screen leaving the map and leaving edit mode let go, counted for the frame snapshot; an untracked hand holds its item still. * openxr: held screens follow the hand at the headset's rate The main thread publishes what each hand holds through a seqlock; each headset frame re-places a held item, both eyes of a stereo screen, from that frame's own aim, so it no longer waits for the next video frame. An item let go stays where the hand left it until a quad set planned at or after its release (an edit count carried in the frame snapshot) shows it, even once the hand holds another item, so nothing steps back. Lazy Follow holds its glide while anything is held. Edit mode outlines every item, brightly the one under a laser, and draws the menu's handle bar under it, from the laser's image, now an atlas of the dot, two outlines and two handle bars. They take only the layers the screens leave after the dots. * openxr: edit mode end-to-end cases and documentation The test layer's aim can roll, and the e2e runner drives edit mode against Monado: grabs that follow the hand on every headset frame and stay where they are let go, level and tilt, the stick, resets of a screen and of the menu, the menu taken by its handle and following Screen 1 at its size, outlines and the handle, a no-map core, a views core's fallback frame, Follow Head, Lazy Follow, recentering, two hands, pausing, and what starts and ends edit mode, including the headset's hotkey bits with no command interface. The cases whose work is a host's or driver's own run on glcore and gl too. docs/openxr.md explains editing screens in the headset and where the menu goes. * openxr: a laser beam from each controller to its dot A thin strip runs from each hand's aim to its dot, or 1.5 m into nothing, turned about its own axis each headset frame to face the head so it never shows edge on. It comes from the laser's image, so it needs no new swapchain, and uses layers left after the dots and edit mode's outlines, so it is the first to go when the runtime has few. The new Laser Beam setting shows it in edit mode only (the default), always, or never; outside edit mode, Laser Pointer and its hotkey hide it with the dot. * openxr: edit mode outlines in three bands, outside the item A light band between two dark ones shows on white, black and busy borders alike. The rim now sits wholly outside its item, 5 mm clear of it even after filtering, so it never covers the picture; its region grows to keep two texels a band at about the old thickness. Screens' outlines are submitted before the screens, so a neighbour placed flush covers the part of a rim over its picture, while the menu's outline and handle bar stay on top. The handle bar moves 5 mm below the menu's outline, which it would otherwise cover, and gets the same bands, faint and bright. * input: choose a headset edit item with Select Next and Previous Edit mode steps from the laser through the screens it can move and the menu. A chosen item is the one lit, either hand's grip takes it from wherever the hand is, and Reset resets it, so a screen hidden behind another can be moved. Whatever a hand holds stays lit. A ray on the menu's handle bar takes the menu before any screen in front of it. Within 3 cm of the bar, a ray that came from the bar keeps the menu even over a screen, and one that came from elsewhere takes it only where no screen is hit. * openxr: snap a held headset screen to the screens near it Snap to Screens, on by default, turns a held screen parallel or square to a nearby screen and lines up its plane, edges or centre. Each pull is exact within 3 cm or 5 degrees and fades out over as far again, so the screen catches up with the hand rather than jumping, and snapping fades in over the first 5 cm or 10 degrees after a grab, so taking a screen never moves it. With several screens near, the snap that leaves the least error wins, so a near screen's exact pull beats a far one's fading pull whatever their order. One stateless function inside the grab serves the main thread and the headset's thread, so a screen is saved where it was shown. The holding hand buzzes as a snap takes hold, once, and not again until the screen has left that pull. * input: turn a held headset item with the free hand's stick A quarter turn of a held screen needed the controller turned a quarter turn, which swung the screen round the hand on its long reach, so a square snap could not be reached. Now the stick of the hand holding nothing turns what the other hand holds about the vertical through its middle, faster the further it is pushed, and snapping still catches it parallel or square. A carried item keeps its pitch and roll and its angle to the way the hand points across the floor, so raising, lowering or twisting the hand no longer tips or swivels it; the trigger lets it ride the hand rigidly. * openxr: smooth the controller aim in edit mode A screen held about 2 m out on the laser magnified every tremor of the hand. In edit mode each hand's aim now goes through a One Euro filter, which smooths a slow hand hard and a fast one little, with its rate taken from the headset frame's own time step. The headset's thread filters once a frame and hands the result to the main thread, which grabs, moves and lets go from it, so a screen is saved where it was shown. The new Edit Smoothing setting picks Off, Low, Medium (the default) or High; outside edit mode the lasers are never smoothed. * openxr: name the chosen edit item beside the controller Edit mode now shows a faint tag just above the hand that last pressed Select Next or Previous, reading Laser, Screen N or Menu, so what the grip will take is plain without a notice. The words are drawn once into the laser's image in the default font and the interface language, light over a dark halo like the outlines so they read on any background, and the tag is one more headset layer; when the runtime is short of layers the beams are dropped before it. On a runtime whose swapchains are too narrow for the words, the laser's image keeps its old width and there is no tag. * input: tilt a held headset item with the free hand's stick In edit mode the free hand's stick already turns the item the other hand holds. Its up and down now tilt that item too, about the item's own left-right line through its middle, at the turn's rate and with no limit: up tips the top back, as when pushing a monitor's top back. It works whether the item is carried or rides the hand with the trigger held, and snaps as the turn does. Within about 25 degrees of left or right the stick only turns, and within about 25 degrees of up or down it only tilts, so a push meant for one does not drift into the other. Edit mode's starting notice lists the turn and tilt and stays about 8 seconds. * input: snap a turned or tilted headset item to angle steps While the free hand's stick turns or tilts a held item in edit mode, the item clicks into steps of the new Snap to Angles setting (15 degrees by default; 30, 45, 90 or Off), so it is easy to set straight. The turn counts from facing the user's head across the floor, the tilt from upright. Within about 2 degrees of a step the item holds there and the holding hand buzzes once; the stick's own angle keeps adding up, so pushing on leaves the step. Only the stick steps: carrying an item or tilting it with the trigger never clicks, and a snap to another screen that would engage wins over a step. * openxr: show the back of a headset screen turned away Runtimes draw only the front of a quad layer, so a screen or the menu turned away from the head vanished. When the head is behind a quad's plane, the frame now submits a plain dark panel in that quad's place: a solid grey cell of the laser's image, at the quad's size, turned to face the head. It takes the quad's own layer, so the layer count and cap are unchanged. Edit mode picks either face, so an item turned away stays grabbable, and its outline, handle and dot turn with its back. * input: keep a screen snap's angle once the free stick lets go A snap to another screen shows a held screen square or parallel to it, but the item's own angle stays where the stick or the hand took it, up to several degrees off. The next turn with the free hand's stick started from that hidden angle, so the screen left the snap early on one side, late on the other, and caught up with a lurch. Now, as the free stick returns to its deadzone and as a push starts, a snap engaged on the turn becomes the item's own angle: the carried turn, or the orientation the trigger tilts. A snap still fading in never does, since that would hop the screen into it. The place is left to the hand, and carrying alone never keeps the angle, so pointing still pulls a screen out of a snap. Letting go of the grip leaves the item where it shows, as before. * openxr: hinge a held headset screen to a neighbour's edge Snap to Screens now also hinges a held screen to another along a shared edge at any angle between them, so a DS or 3DS pair can be opened at a slant: an edge within 5 degrees of parallel to one of the other's, within 3 cm of its line and sharing some length with it, turns exactly parallel and moves onto it, centres or ends level, its angle left free. The pull fades in over the first 3 cm of shared length, so a screen slides off the end of the other's edge without a jump, and a diagonal neighbour never hinges. Screens that would overlap do not hinge; the pull is whole from flat to square and fades out folded shut. Flat and square snap as before and report a hinge at 0 and 90. A turn to the same screen pivots on the shared edge, so the edge stays on the line as the screen leaves flat or square. The snap still keeps nothing between calls; its result carries a hinge bit, and video_xr_snap_hinge also says which edge and line, for the stick to swing about. * input: swing a hinged headset screen about the shared edge While a held screen is hinged to another, the free hand's stick axis that turns nearest the shared edge, the tilt for a top or bottom edge and the turn for a side one, now swings it about that edge, so it stays attached; the other axis still turns or tilts it and can pull it off. Where the swing leaves it, angle and place, it stays as the stick lets go. Snap to Angles counts a hinged screen's steps from flat with the other screen. A hinge counts only while the screen's own edge is within its range, not when other snaps alone hold it on the line, so a swing never carries a screen round a line it is far from. A square corner is a side hinge at 90 degrees, so the turn stick there now swings the screen about the corner's edge. Main thread only; nothing new reaches the frame loop, which still shows the grab through video_xr_grab_pose. * openxr: leave a gap between hinged headset screens Edges that meet in edit mode, side by side, at a corner or hinged at any angle, now click to a set gap from within 1 cm and otherwise keep the gap the hand gives them, still hinged, up to 10 cm (letting go by 13). Screens no longer have to touch, which made them overlap. Each grab carries the gap, so the edit poll and the frame loop snap alike; until a setting sets it, it is 0. Pushed past touching, a hinged edge stops there for the first centimetre of the push, the stop fading out by 3 cm, and ticks once per push. A swing pivots on the held screen's own edge, so the gap keeps through it, and a commit keeps the hand's gap unless the click holds it. After a swing lets go past square, that stick axis and its steps keep pivoting on the same edge until the edge leaves the hinge's reach, so a flick past square and back does not creep. Of two neighbours within the 5 degree turn snap, the nearer edge wins the turn and the hinge, by one measure for every screen, so a screen stacked behind, one off the ends of the edge or a side wall at a corner never outranks a hinge the turn holds. * menu: add Hinge Gap for headset screens Hinge Gap (0, 1, 2, 3, 5 or 10 cm, default 2) sets the gap edit mode's Snap to Screens leaves between screens that meet edge to edge, side by side, at a corner or hinged. Edit mode takes it as it starts and gives it to each grab; the click to it buzzes like the other snaps. * menu: put Edit Screens under Headset Output Stereo 3D & Screens now lists Edit Screens and its snaps right under the switch that reveals them, then the headset's own setup, then where the screens go, with the per-screen rows last and Visible first among them. * openxr: track each held screen's hinge on the frame loop The frame loop already re-snaps each held screen every frame. It now also keeps, per hand, the hinge that snap holds whole, until the edge leaves the hinge's 4.5 cm reach, where the frame draws it, so edit mode can mark it. A kept hinge whose target no longer matches is dropped. video_xr gains the hinge line's ends, along the overlap of the two real edges and midway across the gap, an outline cut that drops one edge's rim, and a green strip in the laser's image, before the tags. * openxr: mark a hinged headset screen with a green line While a held screen's hinge holds, the two edges that meet drop their outline rim and a 6 mm green line runs along the hinge where they overlap, midway across the gap, so it is clear when the hinge has taken hold. The line is drawn as the beam is, a centimetre nearer the eye than the edges, since SteamVR does not keep the order of overlapping quad layers, and comes after the outlines in the layer budget; hidden rims cost no layer. * input: buzz longer as a headset screen hinges A hinge taking hold now gets one 150 ms buzz at 0.8 on the hand carrying the screen, against the 20 ms snap tick, so the attach can be felt. It fires again only after the edge leaves the hinge's reach, hinges elsewhere, or the other hand takes the screen it hinges on, so a swing past square and back does not repeat it. Ticks due while it plays are skipped rather than cutting it short. * openxr: find headset screens resting in a hinge Two screens no hand holds rest in a hinge when their poses pass the test that engages a carried screen's hinge, with either one carried, and the hinge, held whole with nothing else pulling, leaves the carried one where it is. So a hinge found at rest stays hinged when its screen is taken. The engaged test moves into its own function so both use the same one, and one target can pull whole whatever the grab's fade. * openxr: keep a headset screen's hinge shown at rest In Edit Screens, two screens no hand holds that rest in a hinge keep the green line and their hidden rims, whether let go hinged or loaded that way. The main thread counts them only when a resting screen changes and sends them with the hands' record, so a release and its resting pair arrive together; the frame loop places each line from the screens it shows. A screen hinged at rest on two adjacent edges still keeps one of those rims. The hands' record now outgrows the quads, so the seqlock's copy is sized to the larger of the two. * openxr: pick up a hinged headset screen still hinged Taking up a screen that rests in a hinge starts the carry on that hinge, whole from the first poll, with no buzz and no jump, until it leaves the hinge's reach, another hinge takes hold, or the other hand takes the screen it hinges on. Until the grab's own snapping has faded in, the kept hinge's target still pulls whole, so a straight pull out does not hop. The grab carries the hinge so both threads hold it the same; of several, the one the snap ranks best carries over, and only where holding it moves nothing. While the other screens fade in, the hinge's screen also leads the turn, so a nearer one still fading does not take it. * openxr: end a headset screen's hinge where a push passes the stop Pushing a hinged screen into its neighbour holds the edge at touching for 3 cm of push, fading out by 5.5 cm. Where it stops holding, the hinge ends: the line goes, the rims come back, and the lid and buzz are cleared, so screens pushed into each other show no hinge that a let-go there would not rest in. Pulled back, the hinge takes hold again 5 mm short of where it ended, with the long buzz, so a tremor there does not flicker it. The grab carries that through flag, and the frame loop keeps its line until the main thread sets it, so the two threads never disagree for more than a poll. At Hinge Gap 1 a pair let go touching rests hinged and is taken up hinged, as at every other gap. A screen swung into a square corner gets the long buzz on arrival, and the other hand taking the target does not hop the carried screen by the push's depth. * input: let go of a headset screen where it was before the grip opened Opening the grip twitches the hand, and the poll that sees the grip open would move the screen by that twitched aim. Half a degree of pitch moves a screen 1.8 m out by 1.6 cm, enough to knock it off its hinge's gap or out of level, so it would not rest hinged. Each hand keeps a short trail of where, turned and as large as its screen showed, entries at least 10 ms apart, and a grip let go puts the screen as it showed 80 ms before (INPUT_OPENXR_EDIT_REWIND_US), or as it was taken if held for less. Edit mode's end and a recenter let go where the screen shows. * openxr: find where a let-go headset screen settles in its hinge A screen let go while its hinge shows should land exactly in that hinge, so the hinge still shows at rest and a pick-up is hinged. video_xr_hinge_settle holds the hinge whole from the hand's pose, then moves the edge to the set gap from within 3 cm of it (touching stays where the stop holds it) and levels it along the edge within the level pull's reach. Its result must pass the rest test, and it turns no further than a grab's hinge engages, which shares that test. A pair resting inside a settle band but off its point is no longer listed at rest, since a pick-up and let-go would move it. A touching rest is taken 5 mm deeper into the push stop, at every Hinge Gap, so a hand that drifts less than that apart before letting go settles touching rather than at the gap. * openxr: settle a let-go headset screen into its hinge A screen let go while its hinge shows now goes exactly into that hinge: to the set gap from within 3 cm of it, touching from the stop's hold, and level along the edge from within the level pull's reach. It then rests there, listed and green, in the same publish, and is saved there. Elsewhere it stays where it shows, as before. Each trail entry also records where the hand put the screen with no snap and whether a let-go there settles. At the let-go the rewound entry is settled again against the screens as they are now, at that entry's size, and put in one go. * openxr: let a hinged headset screen rest at any fold Snap to Screens' turn to flat, and its turn to square at a corner, is whole within 5 degrees and fades out by 10, so a hinged screen held there shows at twice its angle off less 10. No fold shown in that fade read as a rest: the pair was not listed, a let-go there did not settle into its hinge, and taking it up would have moved it. Where a hand takes a rest, it now holds the screen at the angle the turn's fade shows it from, the inverse of the turn's pull, so every fold rests, is listed and is taken up unmoved. Held snapping is unchanged. * input: keep a headset screen's hinge through a shaking hand Edit Screens' hinge buzz, lid and push stop followed the snap's own engage and reach on every poll. A hand shaking half a degree (1.6 cm at the screen) about a hinge's edge flipped them many times a second, each flip a long buzz. Each hold now keeps one mark: the hinge a let-go would settle into. It takes hold at once, with the long buzz, and lets go only once polls have seen the hand out of where a let-go would settle for 150 ms, each poll counting at most 50 ms, so a stalled main thread or a spell untracked never lets it go alone. The other hand taking its target, hinged elsewhere or not, the target leaving and the let-go end it at once. The lid waits for the mark to go, the push stop follows it, and a let-go while it shows settles into it from where the hand last would have, edit mode's end and a recenter included. * openxr: draw a held screen's hinge as the main thread marks it The grab carries the main thread's hinge mark, and the frame loop draws a held screen's green line and cuts its rims from that mark alone, through video_xr_grab_marked, while it snaps the screen every frame. A frame loop taking the hinge by itself between polls would flicker the line for a hand shaking at the edge of the hinge's range while the mark held. A mark whose target the frame does not show cuts no rim. The other hand taking the mark's target ends it, and the lid, on any poll, tracked or not, so a lost hand's line does not come back when that hand lets go. video_xr_grab_near_hinge loses its past argument, which only the frame loop's keep used. * openxr: keep edit mode's hinge layers in place SteamVR does not keep submission order for overlapping quad layers, so a layer added, dropped or moved to another index can pop the edit outlines in front of the screens, as a wobble at a hinge would do to its line's layer. The hinge lines are fixed slots: one for each hand holding a screen, hand 0 first, then one for each pair at rest. A slot with no line to show is drawn in place from its screen's middle, at rows of the laser image that are empty either way up. The layer count and every index change only at a grab, a let-go or a re-count of the pairs at rest; a mark only changes crops and which rows a slot shows. * input: date a let-go's trail by frame and stop it at a stall Each trail entry is dated by the frame its aims are from, one a frame and per hand, and the release poll records a new frame's aims first. The rewind stops at a stall, entries more than 2.5 frames apart, a frame counted as at least 20 ms so a fast headset's close entries never read as one. Under load a stall of 0.1 to 0.3 s can come just before the release; the entry from 80 ms back would then hold the pose from before it and step the let-go back across all the stall's motion. With Edit Smoothing on, which damps the twitch, a let-go that does not settle into a hinge stays where and as large as it shows: a rewind there would step back by what the smoothed aim still lags. The hinge decision and the settle use the trail either way. * openxr: cut edit outlines from cells instead of turning them Cutting a top or bottom rim from the one outline image turns both outline quads a quarter turn and swaps their width and height, and on SteamVR that can blink every edit outline as the hinge takes hold or lets go; side cuts, which never turn, do not. The laser's image carries twelve pre-cut outline cells after the tags, faint and bright, for a cut top, bottom, left, right, top and bottom, or left and right, so a cut only picks a cell and the quads keep their pose and size. The image is then 5582 texels wide. A runtime whose swapchains are narrower, or that does not say how wide they can be, keeps the turned and cropped outline at 2498 or 1208 wide. One log line at the laser's creation gives the width chosen and the runtime's limit. GL writes the top and bottom cells swapped for its bottom-up rows, and Android's copy of the image is allocated rather than static. The edit-hinge-3ds-narrow e2e case runs the turned cut at RA_XR_LAYER_MAX_WIDTH=4096, on Vulkan and the GL EGL lanes. * video: draw headset screens straight into the runtime's images Each headset screen was drawn into an image of the driver's own and copied from it into the runtime's on every new frame, Vulkan with a render pass and GL3 with a blit. Screens are now drawn into the runtime's image itself, as before per-screen placement, with no image of the driver's and no copy. A screen drawn again with no new frame - resized, made again or moved while paused - replays the last frame's final pass from the chain: build_viewport_pass_replay draws it at the new size from what it read then, the passes' outputs, their feedback and the history that frame saw, with that frame's count. Nothing else runs, feedback does not swap and history stays, so a feedback or history preset holds still while paused. A chain made to replay keeps one more history image, so the frame's end writes where the last frame did not read; the window's chains keep what they did. This also has movie frames reach the headset, which the copy held at the first one, and draws a screen resized while paused at its new size rather than stretching the old image. * openxr: leave what users who never edit see as it was Screens seen from behind turn to their grey backs only while Edit Screens is on, where screens are turned around; outside it nothing changes and no head is located for them. The grabs carry whether editing is on. The menu opens at its own spot, straight ahead of the anchor, as it did. Menu in Front of Screen 1, off by default, keeps it just in front of screen 1 and moving with it. The frame loop copies the main thread's grabs only once their stamp moves, so outside edit mode no frame copies them. A new session clears the frame loop's own copy and passes over what was published before it, rather than publishing itself: the main thread stays the grabs' one writer. On Touch, the left menu button that opens the menu from edit mode is spent until let go: held on as edit mode ends, it is no Start and does not toggle the menu again after a second. --------- Co-authored-by: libretroadmin <[email protected]> |
||
|
|
ef41fe39e7 | MCP: review fixes for frames, RAM, hardcore | ||
|
|
ef3c8db6b3 | MCP: no tools while achievements are hardcore | ||
|
|
35d113d308 | MCP: memory tools read cores without a map | ||
|
|
30a01d0696 | MCP: GET_FRAME, the screen without a file | ||
|
|
2538f1aa0c |
sunxi: screenshots under the menu are of the menu
read_viewport reads the menu's frame while the menu is up, as that is what the layer shows. Lane in samples/gfx/sunxi_display. |
||
|
|
04890bb46d |
sunxi: lay out, turn, pace and lend frames; fail cleanly off sunxi
The layer's window is where the frontend lays the frame out - aspect, integer scale, custom viewport, cut where it leaves the screen - set only when it moves. A rotation is turned by the CPU into the page. A frame is drawn only into the page not on the screen, as the vsync thread now only reports the flip that landed; without vsync a frame meeting a flip in flight is dropped, and a framebuffer that cannot wait for a vsync ends the thread instead of spinning. An XRGB8888 core is lent the page to be shown next. Messages and statistics are blended into the page; screenshots read it back. Pages are XRGB8888 past the console whatever its depth, laid out only where the framebuffer has room for two; the menu converts at the end of the framebuffer, past them, and shows before any core. Init fails cleanly off sunxi hardware or without a free scaler, and the layer ioctls pass their arguments as the kernel reads them, unsigned long. Lanes in samples/gfx/sunxi_display; docs/video-driver-sunxi.md. |
||
|
|
ed798b5144 |
gfx: CPU-composed views turn each view in its own place
video_views_cpu_t carries the quarter turns its views are drawn at: each view is turned before it is scaled into the placement the layout gave it for the core's rotation. dispmanx sets it; the other CPU drivers leave it at zero. Lane in samples/gfx/video_views. |
||
|
|
60b3380f9a |
dispmanx: on-screen messages and statistics on layers of their own
The message and the statistics are each drawn at the screen's resolution on a DispmanX layer at the top, as large as the text: drawn and uploaded only when the text, its place or its colour changes, into the one of two resources not on the screen, which the next flip shows. A layer that outgrows its resources gets new ones; the old ones go once the flip that took them off has landed. |
||
|
|
5c30ef7817 |
dispmanx: input overlay on HVS layers, display by monitor index, screenshots
Each overlay image is an element of its own over a resource uploaded once at load; the HVS crops, scales, mirrors and blends it, and a move, fade or toggle changes the elements in the next flip's update. An image switched off, transparent or off the screen has no element. video_monitor_index picks the firmware display (default, HDMI 0, HDMI 1, LCD, composite), falling back to the default when it is not there; the screen size is asked of that display. Screenshots snapshot the screen as composed and cut it to the viewport. |
||
|
|
8c93843ffb |
dispmanx: lay out, turn, filter and lend frames as the frontend asks
The frame's element goes where the frontend lays the frame out - aspect, integer scale, custom viewport, cut where it leaves the screen - and moves there with the next flip. A core's rotation is a quarter turned by the CPU and a half by the HVS. video_smooth is taken at run time and a nearest kernel is put back on exit; a smooth start leaves config.txt's kernel alone. Without vsync, a frame that meets a pending flip is dropped rather than waited for. Rows reach the firmware at its own pitch: a lent framebuffer and the threaded wrapper's frame memory are laid out at it, other frames once into the surface. RGUI blends by its own alpha on a layer above the frame. Lanes in samples/gfx/dispmanx_display; docs/video-driver-dispmanx.md. |
||
|
|
dda31fef21 |
Temporal Upscaler: DLSS loads once; Neural Rendering toggles in place
DLSS's feature libraries, Super Resolution's and Neural Rendering's, stay loaded once NGX has loaded them, so NGX set up again on the next content's device or after a driver restart finds them in instead of loading and checking them again. NGX's start-up, and the making of each feature, is timed in the log. Turning Neural Rendering on or off no longer restarts the video driver: its device extensions are asked for (once a run) whenever its library is in the DLSS directory, and the core asked about it then too. Turned off, its model is kept, and turning it on again takes the next frame. |
||
|
|
11370445cf |
Temporal Upscaler: DLSS takes frames in any of its modes; less at start-up
A core that renders in steps, such as a multiple of the console's resolution, can land outside the one DLSS mode's range and was shown without upscaling, saying nothing. The request now names the range DLSS takes over all its modes, and a frame outside its mode's runs in the mode whose range it is in, the nearest to its size; a frame no mode takes is said once in the log. NGX's extension names are kept for the life of the process instead of asked again, with its feature libraries loaded and checked, every time a context is made. Neural Rendering's library is asked about only while it is turned on; turning it on or off now restarts the video driver. |
||
|
|
ccd885c4f2 |
Temporal Upscaler: DLSS Neural Rendering
DLSS 5's Neural Rendering model runs over DLSS Super Resolution's output when its library, nvngx_dlssnr.dll, is in the DLSS directory. It goes through the same NGX session as Super Resolution: the driver's NGX core loads the library from the directory it is given at init, creates the feature (18) on the frame's command buffer and evaluates it on Super Resolution's output with the core's depth and motion vectors at render size. Cores before its driver (616.64) have no such capability; the session then marks it unavailable and Super Resolution goes on alone. Settings > Video > Scaling > DLSS Neural Rendering holds the switch, off by default, and the model's tuning: preset, style, intensity, local structure, local tone, skin structure and auto skin mask. The entry is shown only with DLSS selected, the library found, NGX not OptiScaler's, and no session having found the driver without it. The settings reach the backends as one word. The model reads its tuning only at creation, so a changed setting rebuilds the feature once it has held for 30 frames; turning it off retires the feature through the same ring as Super Resolution's. System Information and the log name the library's version, read from its file without loading it, or say the driver needs updating. The upscaler samples drive both backends against stand-in NGX cores that know the feature: the tuning at creation, the evaluate's inputs and guide sizes, the rebuild after a change holds, the release when off, and a core without it; the loader sample finds the library and reads its version. |
||
|
|
b47f5820bb |
Temporal Upscaler: load OptiScaler from the DLSS directory
OptiScaler had to stand beside retroarch.exe under a system DLL's name (winmm.dll) to hook NGX, which put it in every session whatever the core. As OptiScaler.dll in the DLSS directory it is now loaded only when a core asks for the upscaler with DLSS set, just before the NGX core library it hooks; the status names it without loading it. |
||
|
|
63dae1fe01 |
docs: install OptiScaler as winmm.dll
Next to retroarch.exe as nvngx.dll OptiScaler proxies NGX to itself; as winmm.dll it loads at start and proxies NVIDIA's own NGX library, which is how it runs DLSS on Vulkan. |
||
|
|
e5241e5cf7 |
docs: setting up DLSS, FSR and XeSS
An end user's guide to the temporal upscaler: where to get each vendor's library, where it goes, the settings, the status line and OptiScaler. |
||
|
|
22ee469999 |
MCP: schema check, readable MENU, smaller tool list (#19878)
* MCP: schema check on all tools, readable MENU * MCP: MENU marks choices, answers when closed * MCP: MENU types, shows dialogs; drop menu option * MCP: fold INPUT, OPTION, CORE_OPTION; trim list * MCP: MENU path, more, tab names; clearer errors * MCP: menu title as a text line; trim the list * MCP: trim STEPS' descriptions * MCP: INPUT says the core gets what is held * MCP: MENU review fixes |
||
|
|
6868062dc3 |
exynos: apply a core's rotation, mirror overlay images
The G2D calls libdrm offers cannot rotate, so a core's rotation is now applied by the CPU as the frame is copied into a GEM buffer of its own, and the turned frame is placed at its turned size. Views are drawn as one frame while a rotation is set; the menu stays unturned, as in the other drivers. They cannot mirror either: an overlay image whose quad runs opposite to its texels is written mirrored into its buffer when that changes, and drawn from there. exynos_g2d_test checks all three turns and the mirrored quads. |
||
|
|
fbb4548c7c |
exynos: read frames from the core's memory, blend the input overlay
Where the G2D sits behind an IOMMU, it reads a core's frame from the core's own memory as userptr, and the frame is not copied. The kernel keeps a mapping per address and size and syncs the caches only when it makes one, reusing it unsynced after that, so each frame gives its address another size and is mapped afresh. Memory the kernel will not map is copied from then on, as is a frame that leaves no room on its last page for a second size. Without an IOMMU frames are copied as before. The input overlay's images each get a GEM buffer, premultiplied, with their alpha applied when it changes, and the G2D scales and blends them over the page, behind the menu or over it. Where an image lies outside the frame, that area is cleared first so it is not blended over what it left there on that page. exynos_g2d_test models the kernel's reused mappings and checks both; the overlay setter check now reads this driver, and the compile lane builds its overlay code. |
||
|
|
2d31684683 |
exynos: aspect, zero-copy frames, dirty-rect text, mode switches
The frame was placed at its pixel aspect: a 256x224 frame on 1280x720 filled 822x720 rather than the 960x720 4:3 the core asks for. It now takes the core's aspect, the one chosen in the menu, and the CRT switch's, and is placed again whenever they change. A core that renders into the frontend's buffer is lent the GEM buffer the G2D reads the frame from, so its frame is never copied; a core that reads its frame back is refused, as the mapping may be uncached. The cached frame is retired before that buffer is replaced. With vsync off, a frame that came before the last flip finished neither waited for it nor queued a flip behind it: the driver blocked on the flip every frame. It now drops that frame and returns. Messages and the statistics, which the driver did not draw, get a buffer of their own rather than the menu's. Only the area the glyphs cover is cleared and blended, where the frame's info puts them, in its colour; the font follows video_font_enable, _path and _size. The menu's alpha is applied as it is copied. Screenshots read the page on screen. A mode switch and a CRT modeline build new pages at the new size, the old ones removed only once the new ones are up; video_fullscreen_x/y pick a mode at the refresh rate. The display server is told it is on KMS, and of the mode. The framebuffers were removed with the GEM handle as the fd, so none were; init no longer leaks the card when no display is found, nor crashes on a card that reports no version. samples/gfx/exynos_g2d runs the driver against a fake kernel and a G2D that draws into the GEM buffers, in the samples/gfx workflow; its 23 checks fail on the driver before this. The driver reads no live settings from the video thread any more, so its exemption in video_thread_state_check goes, and the doc drops the forked libdrm and kernel it no longer needs. |
||
|
|
be9f97d2cf |
MCP: review fixes, checkpoints, queued STEPS (#19861)
* MCP: cold paused, held input, stacks, arg types * MCP: checkpoints, prompt, completions, records * MCP: completion errors, sized prompt, CI shaders * MCP: rollback via loader, STOP n, completion fixes * MCP: ask input_driver whether a replay records * mcp: Clarify steps, and ensure MCP standards * MCP: queued STEPS, checkpoint step, 32 steps * MCP: queued STEPS say how long they sat idle * MCP: queue ends on load/reset; simplify * MCP docs: queue stops, idle, list caching * MCP: shorter STEPS wording * MCP: notice when port taken, --command, taps * MCP: drop the play_game prompt and completions * MCP: queue, tap and reset fixes from a review * MCP: queue outcomes, hex values, field fixes * command: --command takes an empty host or port as the default "CMD;;PORT" and "CMD;HOST;" fill in the default host or port, so an empty segment after a ";" counts as a host or port field again. A STEPS list still keeps its own ";" steps, which carry a space, a "|" or are "checkpoint". --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
7f86c7f53c |
MCP: STEPS, PRESS until, and fixes after WATCH (#19850)
* MCP: answer once when reply_to answered already * Commands: refuse slots past INT_MAX * MCP: follow-ups to WATCH and the folded tools * Commands: take CRLF lines; keep long family names * MCP: fixes from a review of the server * stdio_e2e: tool list size after the combined fixes * MCP: lone CR, over-long stdio lines, slot cap * MCP: stdio keeps its agreed revision * docs: MCP server doc matches the code again * Leave CHANGES.md out of this change * MCP: STEPS, PRESS until, 16-bit waits, 16 watches * MCP: 4 kB off tools/list; CORE; LOAD_CONTENT paused * MCP: refuse unknown params; taps; review fixes * MCP: GET_LOG, READ, RUN_UNTIL say what they mean * MCP: LOAD_CONTENT's frame back under budget; STEPS counts its taps LOAD_CONTENT copies the content path, to the heap, only where a pause setting follows it, which keeps its stack frame where it was. STEPS counts the frame let go between two presses of a button against the frames one request runs, so a list it accepts can finish in them. Lane: the taps case in samples/network/command_frames. --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
75f278e286 |
mcp: WATCH a step and report what follows (#19834)
* WATCH: a step and the look that follows it, in one request A field run's steps were a press and then a read for each thing it needed to know: where the player was, where the enemies were, which mode the game was in. Four requests to take one step, and the reads were the bulk of a hundred calls for one level. WATCH <label> <address> watches a byte, and RUN_FRAMES and PRESS carry every watch along with their own answer, as a line of label, address and value. A step needs no reads after it. The lines land where the rows machinery already looks: both commands answer with a line that opens with their own name, which the row reader skips as a header, so the watches are the rows and the two commands declare the shape they now answer in. A failure stays its reason and carries no reading. Each watch is resolved when it is set, as RUN_UNTIL resolves the byte it waits on, so an address nothing maps is refused there rather than read later. Content closing drops them all, which is what invalidates where they point. Eight at once; a label already watched keeps its place in the list when its address changes, so rows stay where a client saw them. RUN_UNTIL is left alone. It answers with the byte it waited on in three columns of its own, and giving it watch rows would collide with those - its format landed days ago, and changing it is a separate decision. Also the test PRESS never had: it lets go only of what it pressed itself, so a button held with SET_INPUT survives a press of the same button. That was fixed by eye on the branch before #19815 landed and has been unguarded since; GET_INPUT makes it three lines. tools/list is 24715 bytes at 49 tools, from 22944 at 48. A tool's own rows schema is about 450 bytes and this adds three of them, which is most of the difference; the budget in the end-to-end test records what each phase has cost it. * WATCH: keep RUN_UNTIL rows, range-check addresses * WATCH takes pairs; RUN_UNTIL carries watches * Memory commands: check bytes and addresses first * MCP: MENU, DISK and REINIT, three tools for 12 * SET_OPTION: drop streaming's unused event * stdio_e2e: cap each tool, lower the total * MCP: STATE and REPLAY tools; files are commands * Review fixes: bounds, ERANGE, live watch lookups * STATE: answer as STATE, not the command it runs * MCP: family tools answer under their own names * RUN_UNTIL: look its byte up every frame, as WATCH does RUN_UNTIL keeps the system address it waits on and reads the byte through the memory map each frame, so a core that moves its memory is followed, and one that stops mapping the address ends the wait with an error. VALUE carries the byte as last read. The step reply is clamped to its buffer, and the deferred reply's family sits ahead of its generation counter. command_frames covers a remapped and an unmapped byte under RUN_UNTIL and WATCH and the watches dropping when content closes; mcp_server pins the structured RUN_UNTIL and step replies against the tables command.h ships. --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
cc4de1186c |
RUN_UNTIL: wait nine seconds of the content's frames by default
With no frame count given, RUN_UNTIL waits about nine seconds of the content's own frames, capped at 600, so its answer arrives inside a structured interface's ten-second reply deadline at any frame rate: 540 frames at 60fps, 450 at 50fps. A request can still ask for up to 600. The command_frames lane checks the default against its core's 60fps. |
||
|
|
a4a72b3c33 |
RUN_UNTIL: range-check the value and frames before narrowing
The value and frame count are compared as the unsigned long strtoul returns, so an argument past 32 bits is refused instead of wrapping to an accepted one, and spaces after the value no longer read as a frame count of zero. The command_frames lane covers both, along with the < and > conditions. The LOAD_CORE comment moves back onto command_load_core, and the docs say hardcore mode refuses SET_INPUT, not GET_INPUT. Closes #19833 |
||
|
|
297986dd43 |
GET_INPUT, and core paths checked before anything is unloaded
Three things probing master turned up, and a trim that pays for two of them. GET_INPUT [player] answers what a player holds without changing it. There was no way to ask: SET_INPUT answers in that shape but has to set something first, and RELEASE_INPUT lets go before it answers, so it always reads none. A client could not ask what it was holding. LOAD_CORE answered "The command failed." for a missing path and for a library that is not a core alike - the generic fallback, because it returned false without ever saying why. It says which now. Neither LOAD_CORE nor LOAD_CONTENT unloads the running core for a path that turns out not to be one. The library is probed first, with libretro_get_system_info(), which is what core scanning already uses and which keeps what it finds, so asking twice is free. Checking the paths stopped a typo costing the game; a stale library or something renamed still did. tools/list pays for every tool's description on every session, so the two new ones are paid for where the list repeated itself: readOnlyHint is false by default and 37 tools said so anyway, which was 770 bytes, and the memory map named hexadecimal in four columns that its own description already covers. 48 tools in 22861 bytes, from 46 in 21449. RUN_UNTIL's conditions move into command.c: a table in the header is a copy in every translation unit that includes it, and nothing outside command.c compares them. |
||
|
|
84bc936dc0 |
RUN_UNTIL: wait on a byte, in one request
A field run cleared one level in over a hundred calls and spent most of them waiting: run a few frames, read a byte, go round again. Until a Goomba was twenty pixels away, until Mario landed, until a transition finished. RUN_UNTIL <address> <condition> <value> [max frames] runs the content until the byte at a system address meets the condition and answers with whether it was met, how many frames ran, and what the byte holds. The loop runs inside the frontend, where a frame costs nothing, rather than across a connection per iteration. It is RUN_FRAMES' job with a predicate instead of a counter: the same frame_work flag, the same deferred reply, the same one-at-a-time rule, so nothing new blocks and there is no second state machine. Reading core memory every frame from the runloop is what rcheevos_test() has always done, on the same flag. Two bounds keep it honest. An address nothing maps is refused before a frame runs, rather than after a wait that could not have ended. The frames are COMMAND_FRAMES_REPLY_MAX at most, and where they run out first it answers "waiting" with how far it got, so the next request carries on instead of the client being told it failed. The harness core now maps a byte that follows its frame counter, so the waiting itself is tested rather than only the refusals: already met answers with no frames run, three frames away answers on the third and not before, and the cap answers waiting. RUN_UNTIL costs 1391 bytes of tools/list, which took it past the budget the end-to-end test holds; the ceiling is raised deliberately, with the cost recorded beside it. |
||
|
|
f8899526b3 |
openxr: add hand tracking API and implementation
Closes #19831 |
||
|
|
c2e5bce004 |
MCP: structured results, and LOAD_CONTENT checks before it unloads (#19815)
* LOAD_CONTENT: check paths before unloading * mcp: results carry structuredContent * LIST_CORES filters by content path * Structured rows for core options and memory map * MCP: do not advertise a cacheable tool list * SET_INPUT holds several controls at once * A row opening with MORE is a row, not a cursor * MCP: fewer tools, and rows a client can rely on structuredContent and outputSchema never reached a client over stdin and stdout. The gate read the MCP-Protocol-Version header, or the version the stateless revision sends with every request; stdio has neither, so the revision agreed at initialize was parsed, used for the handshake reply and then dropped. It now holds for the session, which over stdio is the connection. A command answers with structuredContent exactly when it declared an outputSchema, so a client reads the schema it was handed rather than probing each reply. SET_INPUT, RELEASE_INPUT and GET_LOG declare one now. The "MORE [next]" line was read by a literal in the server. The shape of a reply names its own trailers instead - the lines that carry a number about the reply rather than a row of it - so GET_LOG's NEXT and SKIPPED are the same mechanism rather than two more special cases, and a reply says nothing about being cut short where it cannot be. A reply whose lines are prose declares no columns: GET_LOG's page runs to 60k, and a row apiece would answer with it twice. Thirty-four commands are no longer offered as tools. A hotkey that steps a selection - the next state slot, the previous shader, the next cheat - is a hotkey because a person presses it while watching the screen. Where a command takes the value outright that one is the tool, and stepping towards it is only a way to get the value wrong; where nothing reports the selection either, there is nothing worth offering yet. READ_CORE_RAM and WRITE_CORE_RAM go too: an achievement address looks like READ_CORE_MEMORY's system address and is not one. The disc and the menu are kept, because stepping them blind is all there is and a disc swap is part of playing a game. All of them are still commands on every line interface. The tool list is 45 tools and 20k, from 79 and 23k, and the end-to-end test holds it to a budget: every description is context a client pays for before it does anything. * PRESS: hold, run and let go in one request A tap was four requests: SET_INPUT, RUN_FRAMES, SET_INPUT to release, RUN_FRAMES again. Each is its own HTTP connection against four slots, so a field test spent about 25 of them crossing a menu and about 10 on one jump. PRESS <player> <control>[+<control>...] <frames> holds the buttons, runs that many frames and lets them go, answered once the last frame has run. It is RUN_FRAMES' job with buttons held through it: the same deferred reply, the same one-at-a-time rule, the same pause the runloop applies to frame advance, so nothing new blocks and no second state machine exists. Whatever ends the frames lets the buttons go, including the error that ends them early. Axes stay SET_INPUT's. They are held at a value rather than pressed, and SET_INPUT with RUN_FRAMES is still the way to hold a control across several steps. SET_INPUT and PRESS read the same request - a player, controls joined by "+", and a number - so that parse is one routine now, and both resolve every control before either acts on any. * CHANGES: the MCP and command work in this series * GET_STATUS: say what it actually answers with The description promised the content's CRC32 and the reply has never carried one: it is the system and the content's file name, separated by a comma. HELP prints that text on the line interfaces and tools/list hands it to every MCP client, where it is the prose a client reads to decide how to take the reply apart. One told to expect three comma-separated fields gets two. Adding the CRC instead would be worse. Working it out means hashing the content, which for a core using need_fullpath reads a whole disc image from disk - task_movie.c already carries that note, having hit it - and GET_STATUS is the command a client polls. It would also change the reply for every client that reads the line today. * command: descriptions that match the replies tools/list hands a command's description to every MCP client, and HELP prints it on the line interfaces: it is the prose a client reads to decide how to take a reply apart. Five of them described a reply that was not the one sent. SET_INPUT and RELEASE_INPUT answer with three fields - the player, the buttons held and the axes held - and the description named two. A client splitting on what it was told got the player where it expected the buttons. RELEASE_INPUT did not say it answered at all, and with no player it lets go for everyone while answering for the first of them, which is worth saying rather than leaving to be discovered. GET_LOG can open with SKIPPED and a count where the lines asked for had already been overwritten, which the description did not mention: a client reads it as a log line. READ_CORE_MEMORY and WRITE_CORE_MEMORY both answer - with the bytes, and with how many were written - and neither said so. * READ_CORE_MEMORY: format bytes without snprintf Both memory reads wrote their reply with an snprintf per byte, to turn one byte into two hexadecimal digits. At COMMAND_READ_NBYTES_MAX that is 16384 calls, which costs 1.5ms on an aarch64 Pi - a tenth of a frame spent formatting, on the main thread, in the command an assistant reads memory with most. A nibble table is 0.02ms for the same bytes, 68 times faster, and the output is identical for all 256 values of a byte in both of the formats the two sites used. The harness core now maps its memory, because nothing in the tree did: READ_CORE_MEMORY, WRITE_CORE_MEMORY and GET_MEMORY_MAP had no test that reached past "no memory map defined", so the formatting this changes was uncovered. The test asserts the replies byte for byte, including every digit of a byte, a read clamped at the end of its region, and one outside every region. Breaking the table deliberately fails three of them. * PRESS: leave SET_INPUT's holds in place PRESS lets go only of the buttons it pressed itself, so a button a client already holds with SET_INPUT is still held once the press ends. * command, mcp: trim comments and order new struct members Drop the superseded LIST_CORES and reply-splitting comments, shorten the new comments to what stays true, and order the new struct members by alignment. --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
714db64c8b |
MCP server: stdio transport with log notifications
--mcp-stdio serves MCP on stdin and stdout as newline-delimited JSON-RPC, for a client that starts RetroArch itself; no token is asked. It takes the stdout descriptor for the protocol at option parsing and points stdout at stderr, so RetroArch's log and a core's prints never reach the client's stream. Reads and writes never wait: a line is handled once whole, answers are queued and written as the pipe takes them, and both outlive the server across a content load. It offers logging: after logging/setLevel, new lines from the GET_LOG ring at that level and above go out as notifications/message from the poll, so the threads that log do no more than before. Closing stdin quits. |
||
|
|
ae32b07cfe |
command: GET_LOG, from a lock-free ring of the last lines logged
RARCH_LOG_V copies each formatted line into a ring of the last 256 lines once it is armed, which the MCP server's start or the first GET_LOG does; unarmed, a log line costs one relaxed load more. Any thread may log: a writer claims a line number, writes the slot as atomic words between a busy mark and its stamp, and the reader keeps a line only when the stamp matches before and after the copy, so a reader never waits and never sees a torn line. GET_LOG answers the lines since the one asked for, then NEXT; lines overwritten before they were read are counted as SKIPPED. Desktop builds with the MCP server only; samples/runloop/log_ring writes from four threads while reading, under ASan and TSan. |
||
|
|
0a4aff89ef |
command: RUN_FRAMES
RUN_FRAMES pauses the content and runs it a given number of frames through frame advance, answering once the last has run, so a client can step the game and look between steps. While it runs a frame-work bit makes each iteration a frame advance; after each core_run the command counts the frame off, and closing the content answers it with an error. With the bit clear the run loop does nothing new. |
||
|
|
a808962621 |
command: SET_INPUT and RELEASE_INPUT
SET_INPUT holds a RetroPad button or stick axis for a player, read by the core over the player's own controller through the input state wrapper, until it is set again or RELEASE_INPUT lets it go; replays record it like any other input. A single flag tells the wrapper there is anything to add, so with nothing held a query costs one test. Achievements hardcore mode refuses it. |
||
|
|
d69939f054 |
command: GET_CORE_OPTION, SET_CORE_OPTION and GET_MEMORY_MAP
GET_CORE_OPTION lists the running core's options as key, value and description, and with a key adds the values that option takes; SET_CORE_OPTION sets one to one of those values, changing nothing when it already has it. GET_MEMORY_MAP lists the core's memory map regions with the addresses READ_CORE_MEMORY takes, their masks, kind, access, byte order and address space, so a client can find memory without guessing. All three are tools on the MCP server, described there. |
||
|
|
ea863ca5d4 |
Remove Travis CI leftovers and move driver notes to docs/
Travis CI no longer builds RetroArch; .travis.yml, the encrypted deploy key and the Metal deploy script go. The OMAP, Exynos and Mali fbdev driver notes move from the root into docs/, and gmon.out from profiled builds is ignored. |
||
|
|
5103a232df |
OpenXR: one core for the desktop and Quest headsets, with head tracking, controllers, settings and desktop OpenGL (#19811)
* menu: the cursor stays on Headset Output when Stereo Mode comes and goes Stereo 3D & Screens lists Stereo Mode above Headset Output only while the headset is off. Turning it on moved Headset Output up a row and left the cursor on the row below it, Headset Refresh Rate, so a second OK meant to turn the headset off opened the rate list instead. The cursor follows the row, as it does for Mapped Port when Device Type comes and goes. samples/menu/stereo_headset checks it for OK, right and Start. Co-authored-by: Craig <[email protected]> * openxr: a session without focus is not logged as controllers not read xrSyncActions answers XR_SESSION_NOT_FOCUSED, a success code, while the runtime's dashboard or another application has focus. It was logged as a warning each time focus went; only a failure is now. The test layer answers a sync with a scripted result (sync <n>), and the headset suite gains input-sync-unfocused. Co-authored-by: Craig <[email protected]> * libretro: a TRACKED view map, and HMD is how a core finds a headset A map's shape could not tell a head-tracked eye pair from a stereoscopic screen sent alone. The new flag says which. HMD now means tracking works, and a core learns of a headset from it only. REQUEST_FLAT is a map without the flag, and OpenXR always gives real eye poses, so the flag and the IPD hint go; nothing read either. Co-authored-by: Craig <[email protected]> * openxr: the runtime, session, swapchains and frame loop are shared units The desktop Vulkan host's instance and headset, its session state machine, its swapchain slots and its frame loop with pacing move into gfx/common/openxr_*, which name no graphics API, so every headset host can use them. vulkan_openxr keeps only what is Vulkan's. Co-authored-by: Craig <[email protected]> * video: the headset's host says how it is, and answers the pose calls One host interface reports the headset's state on the desktop and the Quest alike. HMD comes with PRESENTS and STEREO, and only while the session runs. The pose calls go to the host, and a view map sent again unchanged is not applied again. Co-authored-by: Craig <[email protected]> * openxr: a TRACKED map's eyes are the desktop headset's projection Tracked content gets a space of its own, LOCAL or STAGE as the core asks, and the poses sampled in it. The eyes the core draws are submitted as a projection layer with the poses it was given, and the screens a headset leaves to that projection are hidden. A recenter moves a seated world with the screens; a standing one stays where the room puts it, and the core is told RECENTERED. A paused core's tracked world comes back after a driver reinit. The views test core can draw a world-locked grid, for checks made in a headset. Co-authored-by: Craig <[email protected]> * vulkan: the Quest's headset runs on the shared OpenXR core The runtime starts Android's loader and tells the instance of it. A headset frame can be begun and ended inside the video frame, so vulkan_openxr runs in-line for a headset's own context, which says so in its flags. The units that name no graphics API build without Vulkan, and a GLES binding is ready for the GL host. A space a core asks for again is kept, so a cached context keeps its paused world. Co-authored-by: Craig <[email protected]> * gl: the Quest's headset runs on the shared OpenXR core The Quest's ES3 context is its own in-line host: gl2 draws into a window the context owns, shown as a screen in the room, and copies a tracked map's eyes into the headset's projection. The window shows until the first projection. The kept view pair lives in openxr_host, so the GL host keeps a paused world across a reinit as Vulkan does. The quest flavour builds with GLES=3. A stand-in runtime test runs the in-line host. Co-authored-by: Craig <[email protected]> * input: one headset controller module for the desktop and the Quest The headset input's hooks belong to the frame, so any host attaches the module, and the Quest's own controller module goes. Touch's menu button is Start from its press, and opens the RetroArch menu when let go after a second. The menu's laser can point at a window shown whole, mapped through the area the core is drawn in. The stand-in test runs the shared module. Co-authored-by: Craig <[email protected]> * video: headset settings for every headset driver, and Screen Placement The headset's rows show for every driver with a headset path. Recenter works in the menu and reaches the Quest's GL host, and a seated recenter moves a paused world with the screens in the same headset frame. Screens are placed from an anchor of their own: Fixed in Room, Follow Head or Lazy Follow. The Quest asks for a refresh rate that fits the content and repeats each core frame for whole headset frames. Co-authored-by: Craig <[email protected]> * input: the headset's controllers are a controller They take joypad slots with built-in profiles, so hotkeys, port binds, remaps and rumble use the normal input path on the desktop and the Quest. They take the next free slot like any pad, and keep their port when a gamepad is plugged in, without saving that exchange. A saved "Meta Quest Touch Plus Controller" profile and its remaps no longer apply. Co-authored-by: Craig <[email protected]> * gl: headset output for the desktop OpenGL drivers glcore and gl show a headset on Linux. The XR thread holds a context in the window's share group, made by the X11, X11 EGL and Wayland context drivers, and the session binds it through XR_KHR_opengl_enable or XR_MNDX_egl_enable. The video thread draws each image into shared textures and hands it over with a fence through a triple buffer; the XR thread copies the newest into the swapchain and shows it with the quads or the eye poses it was drawn with. glcore draws the screens and the menu through chains of its own and a tracked map's eyes as the projection, as Vulkan does. gl shows its window, whole or cut into an untracked map's screens, as the Quest does, and tracked eyes as the projection. Both follow the headset's pacing: the XR thread wakes the core for its next frame after taking the last, so none is shown a headset frame early. OpenXR configures with OpenGL alone, without Vulkan. The test layer captures OpenGL swapchain images, and the headset end-to-end runner has lanes for both drivers on GLX and EGL. Co-authored-by: Craig <[email protected]> * docs: OpenXR headset output, and how a headset or graphics API is added What the headset path does and how to build and turn it on, what a core sees, which units are shared and what each of the three hosts adds, and the steps for another headset, graphics API or platform. Co-authored-by: Craig <[email protected]> * audio: pass the reinit flag to MMDevice watchers XAudio2 and DirectSound must give mmdevice_thread the frontend atomic flag. Passing the driver object overwrites its watcher handle when an endpoint changes. Exercise both thread backends against the extracted production spawn functions. * test: reject video backend fallback in the threaded harness Fail early when HARNESS_VIDEO_DRIVER differs from the initialized backend so a missing driver cannot qualify another backend's lanes. * Animated previews: keep high-bitrate audio fed across loops Size the audio lookahead from the preview bitrate, prime it before playback, and refill the full window on rewind. The audio demuxer walks interleaved video packets too; a fixed 2 MiB window stalled on large H.264 packets and left high-bitrate previews silent or delayed after looping. Add a real feeder/mixer regression using generated oversized video packets. Verify that audio continues through three complete loops; the previous feeder fails this test. Validate both reported 4K clips locally on Windows and Linux. * wdmks: bound threaded writes by the device fragment A small write can exhaust its request-sized wait budget before a healthy WaveRT pin's next notification. Size the threaded ring waits from rt_ahead instead, and cover writable waits and blocking writes against a 64 ms fragment. * CI: views end to end on glcore and Vulkan under Xvfb run.py gains RA_E2E_XVFB (Xvfb in place of gamescope, llvmpipe and lavapipe) and RA_E2E_ONLY (a case filter). The validation job runs a subset of the cases. * Views: one compositor for gl3 and Vulkan gfx/video_views_draw.h holds the chain pick, the placement loop with the interlaced row shift, and the canvas and UI target lifetime. Each driver includes it with its own calls bound in by macros, so nothing goes through a function pointer. The UI layer's predicate, per-eye test and dims swap move to video_views.h. Output is unchanged. * Threaded video harness: the frame on screen is the core's newest A frame lag lane checks, end to end, that what the driver draws is the frame the core pushed last: the harness core stamps each frame with its number in eight full-height bands that survive scaling and filtering, and the lane reads back the driver's own upload (the cached frame dropped first) and decodes it. Copied, lent and lent as a cropped window, with threaded video off and on: with nothing in flight it must be the newest pushed frame, and with frames still in the wrapper's ring at most one behind. A zero-copy path that draws a buffer the core has since rewritten, or skips an upload, or a wrapper that holds frames back, fails it. It runs on vulkan, gl and glcore, desktop and GLES 3, in the existing harness jobs (release, ASan+UBSan, TSan, the validation layer). * Fix MSVC preprocessing of video views UI checks Move widget conditionals outside VIDEO_VIEWS_UI_WANTED calls in the GL3 and Vulkan drivers. The traditional MSVC preprocessor treats directives inside macro arguments as tokens, breaking the Windows build. * Provide audio state in the DirectSound pump harness The device notification thread now uses the audio state reinit request. Supply a persistent harness-owned state through audio_state_get_ptr so the standalone MinGW test links. * Preserve RetroArch when building the threaded video harness Request the link command with make -W instead of deleting the frontend executables. The later video views end-to-end tests need the existing RetroArch binary to launch. * ATRAC3plus: accept implicit master code-table indices Master channel code-table mode 3 has no payload and uses zero indices. Return before consuming the optional value count bit, rather than rejecting valid frames and losing bit alignment. Add a synthetic regression covering float, s16 and reset decoding. * Expose hardware H.264 decoding with CPU planes to cores Provide an experimental versioned frontend decoder interface with Vulkan and D3D11 I420 readback. Reserve private video queues through Vulkan device negotiation v2, and retain the decoder backend with cached devices across video driver reinitialization. Keep software fallback for unavailable or disabled hardware decoding and cover readback in decoder and GPU tests. * ATRAC3plus: distinguish coded and declared subband flags Use coded subbands for stereo swap and negation flags and declared subbands for window shapes. Add a synthetic audible stereo regression to detect parser misalignment when fewer subbands carry spectra. * D3D11: views The driver draws a core's views through video_views_draw.h, straight from the frame as the stock chain draws it: placed in each eye's area, blended through a canvas for anaglyph and interlaced, with the UI drawn once into a layer composited into each eye. With a preset or HDR output the frame is drawn whole. Windows-wine-views.yml runs the views end to end on it: run.py gains RA_E2E_WINE, for retroarch.exe under Wine with vkd3d's shader compiler, and the test core builds as a DLL. * D3D12: stock pipes made for the swap chain's format The pipes were made before the swap chain, whose formats were still unset, so every stock draw had an UNKNOWN target format: Windows drivers draw it anyway, vkd3d (Wine) draws nothing. They are made after it now. * D3D12: views The driver draws a core's views through video_views_draw.h as the D3D11 driver does: straight from the frame, through a canvas for anaglyph and interlaced, with the UI drawn once into a layer composited into each eye. Its SDR pipes keep coverage in alpha, which the layer is composited by. With a preset or HDR output the frame is drawn whole. Windows-wine-views.yml runs the views end to end on it as well, on Wine's vkd3d over lavapipe. * openxr: apply unified headset support to current master Apply PR #19811 at |
||
|
|
1682b6bcd4 |
MCP: tools take named parameters, not one string (#19792)
* mcp: tools take named parameters, not one string * mcp: preserve explicitly supplied empty parameters Apply defaults only to absent parameters and retain separators after empty operands. This lets SET_SHADER accept an empty preset to disable shaders. * mcp: normalize named integer parameters before dispatch Convert integral JSON decimal and exponent forms to plain decimal digits without floating-point rounding. Reject fractional values, numeric strings and expansions beyond the request limit before running handlers. Keep the legacy opaque argument path unchanged. --------- Co-authored-by: Codex <[email protected]> |
||
|
|
ce5c333180 |
MCP: Options instead of toggles, correct error handling (#19731)
* tests: cover MCP option state and speed changes Exercise the shipping option handlers and runloop setters with counted driver events. Cover recorder conflicts, hardcore activity, rejected speed changes and repeated requests without extra work. Run the harness under Linux sanitizers and on Windows in the network CI workflow. * mcp: SET_OPTION/GET_OPTION, real errors * mcp: apply option changes to runtime state Reject recording and streaming conflicts before initializing a recorder. Read hardcore activity rather than its configured preference. Apply speed targets synchronously through runloop setters, sharing the fast-forward side effects with hotkey handling and respecting runtime restrictions. --------- Co-authored-by: Codex <[email protected]> |
||
|
|
9312a11000 | cli: Add a --cloudsync command to sync the Cloud from the CLI (#19504) | ||
|
|
62d5954fbc |
mcp: answer tools once their work is through
A tool whose work runs over later frames answers when that work is through, with what came of it, or with its name, ERROR and the reason: LOAD_CONTENT, START_CORE, CLOSE_CONTENT and UNLOAD_CORE once the load or close ends; LOAD_STATE_SLOT and SAVE_STATE_SLOT once the state is applied or written; PLAY_REPLAY_SLOT, RECORD_REPLAY, SEEK_REPLAY and the replay checkpoint tools once the replay has started or the seek or checkpoint has run; SET_SHADER once the preset has compiled; AI_SERVICE with the service's text. A line-based interface keeps the answers it had. The waiting is one shape, task_notify_t (tasks/task_notify.h), held by the work's owner: the content load, the save and load tasks, the movie tasks, the deferred shader load and the translation service. State and replay work a command starts runs from a main-thread task, outside the core's own input poll. The command interfaces outlive a content load, remade only when their settings change, and what arrives during a load waits for it. samples/tasks/content_load gains lanes for the state-slot, replay, seek and content replies; samples/network/mcp_server follows. Closes #19702 |
||
|
|
a7c4f1e7c1 |
cloudsync: add NFS backend
Use the built-in NFS client for cloud synchronization, including atomic uploads and non-destructive backups. Enable the backend in regular and Griffin builds so it is available on webOS, iOS, and Android. |
||
|
|
d9dfddab4b |
mcp: SCREENSHOT answers with the picture as well as its path
The MCP SCREENSHOT tool answers with the screenshot itself as image content, PNG up to 4 MiB, followed by the file's path as text, so a client on another machine, or without access to the screenshot directory, can see what is on the screen. Past 4 MiB, or for another format, it answers with the path alone as before. The screenshot task reads the PNG it has just written back on its own thread and base64-encodes it there, as the encoder streams rows into the file and keeps no copy; the main thread only hands it on. A command interface takes it through the new optional reply_image_to, which only the MCP server sets; every other interface is sent the text. |
||
|
|
007ebda0e6 | mcp: Add callback to tooling to fix return state | ||
|
|
31f2d6b4e2 |
docs: say what --appendconfig does to the saved config
--appendconfig loads extra config files over the main one. The help text and the man page say that and stop. What they leave out is what surprises people: once loaded, the appended settings are settings like any other, and when the main config is saved - on exit, with config_save_on_exit, or from the menu - they are written to it. Start with --appendconfig once and the next start without it still has the appended values. It is long-standing behaviour that scripts and launchers may rely on, so it stays. Both places now say it, and say how to use an appended config for one session only: put config_save_on_exit = "false" in the appended file. The in-tree test configs in tests-other/ already do exactly that. Checked with a run of each: with a setting appended, the main config has it after exit; with config_save_on_exit = "false" appended as well, the main config is untouched, its own config_save_on_exit included. The man page also gave ',' as the separator for several files. The code splits on '|', as the help text says. |