mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-12 18:45:33 -10:00
fd17192acb66cffdf60960f152fa3dad0e0ca95c
81
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2c80e1abd6 | Add Bluetooth settings visibility option | ||
|
|
026a7aa690 |
iOS screen margins: keep iPad multitasking and iOS-only settings
Follow-up to the scene window and screen margin change. Sizing from the window made the iPad multitasking test compare the view with its own window, which it always fills, so Split View, Slide Over and Stage Manager stopped being detected and the safe area was applied to them. Compare the window with the screen. The four settings were registered on every platform, adding keys to every retroarch.cfg. Registering them under TARGET_OS_IOS, as audio_respect_silent_mode is, keeps them off the others. They also had no sublabel: the description never appeared because sublabels are bound through the table in menu_cbs_sublabel.c. The margins were copied into an NSData, queued to the main thread and stored in an ivar, on a path borrowed from the screen orientation setter. All of it runs on the main thread already, so the layout now reads the settings directly and a change only asks for a layout. That also stops every slider step from re-requesting the orientation, and drops CMD_EVENT_VIDEO_APPLY_STATE_CHANGES, which ran before the asynchronous layout it was meant to follow. Margins are rounded to whole device pixels so the layer is not resampled at a fractional position or size. |
||
|
|
ed38dd6394 | iOS: preserve scene windows and add configurable screen margins | ||
|
|
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
(
|
||
|
|
18128e1fa5 |
menu: a display for each screen, Menu Follows, Menu Display and Notifications
On Android, Separate Screen Windows reads "Game Screens on Other Displays". While it is on, Stereo 3D & Screens lists a Game Screen N Display row for each screen, Menu Follows (Game Screen 1, Game Screen 2 and each further screen the core has, or Separate), Menu Display (only when the menu follows no screen) and Notifications (With the Game or With the Menu). A Menu Follows screen kept from a core with more screens stays until it is changed. A display row offers Auto, each display by name with its product and size, such as "Display: DP Screen (DELL U2720Q 2560x1440)", and Off. OK opens a dropdown of the displays listed now. "Game Screen" and "Display:" keep a game's second screen apart from a panel Android calls Screen-2. The name is what is saved, so the same port is found again whichever monitor is on it; a display no longer listed shows as "Display: <name>" and counts as Auto. video_screenN_display, video_menu_follows, video_menu_display and video_notifications_window are saved in the config and in core and content overrides. They replace video_screenN_window_mode: an old Hidden loads as Off, Attached and Separate as Auto, and the old key is no longer written. The mode setting is gone, and the monitor row is listed on no platform; video_screenN_window_monitor stays in the config for the desktop. Until placement reads them, screen 1 stays in RetroArch's window and the others go by Auto unless Off. |
||
|
|
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]> |
||
|
|
5361e1bc8f |
netplay: lockstep for cores whose states are too large to roll back
Rollback keeps a savestate for every buffered frame and serializes every frame, which a state in the tens of megabytes (Dolphin) cannot afford, and needs states that restore deterministically enough to rewind on. Lockstep runs a frame only once every player's real input for it has arrived and never rewinds, so all delta frames share one state, used only to hand a joiner the game or to resync. Desync checks hash the core's system RAM, or the RAM in its memory map, rather than a whole state; a state is serialized for them only when the core shows neither. Input latency rises by a frame whenever lockstep has to wait and falls again once input keeps arriving early, so it holds full speed over a slow link. A host keeps sending while it waits on a joiner, grows its buffers if the core's state has grown, and starts on the running game instead of reloading it. A client takes a state labeled with a frame whose input it already has, and loads it there. Lockstep is a local choice and plays with rollback peers of this protocol, and it is on by itself for cores past 32 MB of state and for cores whose savestates load but cannot be rewound on, which netplay used to refuse. The Lockstep setting asks for it everywhere. Older clients cannot join a lockstep host. samples/network/netplay_e2e runs a host and a client over the loopback on a test core: sticks synced or not, lockstep, a lockstep host with a rollback client, a desync mended and a 40 MB state. |
||
|
|
a0d408f0ce |
netplay: sync the analog sticks of joypad ports
Netplay sent a joypad port's buttons only, so a core reading the sticks of a joypad port (N64, GameCube and others) desynced or saw them at rest on every remote player. The host now sends joypad ports as analog, three words instead of one, with bit 30 marking them; each side hands the core the joypad device it set, so nothing changes for the core. This is protocol 8. A host with Sync Analog Sticks on, the default, turns away older clients with the out-of-date message rather than letting them desync; with it off, any version still joins. |
||
|
|
5c82bce0ed |
overlay: D-pad and face buttons read a finger's contact area (Android)
A touchscreen reports each finger as an ellipse, not a point; Android gives its axes and orientation (AMotionEvent_getTouchMajor/Minor and getOrientation). A thumb laid across up and right, or resting between two face buttons, covers both - and read by its centre alone presses one. After RetroArch-Lite's contact-area input. "D-Pad Input Method" and "ABXY Input Method" choose how each reads a touch: "Direction" - the way the centre lies, as always, and the default - "Contact Area", every way the contact covers, or "Both". Each tip of the ellipse's two axes presses the way it lies from the area's middle, past a small dead zone there. "Contact Area Size" scales how large a contact counts. Measured in pixels per axis against the pointer's own space; RetroArch-Lite measured both against the screen's height, which skews a contact on a wide screen. Android's reader keeps each pointer's contact beside its position, and the driver hands them to the frontend each poll (input_driver_publish_touch_contacts). A finger with no contact size, and every other driver, read by the centre's way. The rows show on Android, where an overlay has the area they apply to. overlay_contact_test holds the geometry: fingertips, thumbs across two ways, upright and level contacts, magnification, wide screens. |
||
|
|
ed0a530640 |
settings: refresh the hotkey tap delay reference
Include the registered input_hotkey_tap_delay default in every emitted configuration profile. Settings behavior is unchanged. |
||
|
|
e2e41947cc | settings: refresh the stylus overlay default reference | ||
|
|
ccffeb553a |
slang: drop vendored glslang and SPIRV-Cross
rslc is the slang compiler everywhere. ./configure --enable-glslang --enable-spirv_cross links the system's glslang and SPIRV-Cross next to it when both are found, and only then does Settings > Drivers offer a Shader Compiler choice between the two: one setting read per stage picks the path, with nothing copied. The MSVC 2010 project builds Vulkan and rslc. Samples that used the vendored copies run on rslc or the system's SPIRV-Cross. |
||
|
|
f377d2d271 |
settings: refresh the builtin media defaults reference
Record continuous playback and the opt-in CRT and refresh options in all three platform profiles. The emitted settings tables are unchanged; their committed reference now includes these intentional additions. Validation: complete Linux samples/settings workflow passes, including table and unity checks. |
||
|
|
edd71e2cf0 |
media: load SubRip sidecars and render cues in fullscreen menus
Parse UTF-8 SubRip tracks in bounded task slices, retain active-session ownership and publish cue text without reading files on the render thread. Draw cues in texture menus and the RGUI framebuffer, including while transport controls are hidden or playback is paused. Add subtitle enable/delay settings and parser, ownership, drawing and four-menu sidecar coverage. |
||
|
|
74b7b06fa5 |
Android second display output for video views (gl and vulkan) (#19905)
* android: drain the sensor queue with both sensors off
Losing focus disables the accelerometer and gyroscope, but an event
already in the sensor queue stays there, and android_input_poll_user()
only read the queue while a sensor was on. The queue's fd stays
readable, so ALooper_pollOnce() keeps returning LOOPER_ID_USER and
android_input_poll() spins at full CPU. The pause and stop
acknowledgements wait for the next runloop iteration, which never
comes: RetroArch logs "App thread did not acknowledge activity state",
the launcher shows late and the app hangs until it gets focus back.
A swipe home hits it often, since focus is lost before onPause.
Always drain the queue and apply the events only while a sensor is on.
* gl2: make a regrown pass texture as the pass's first one
gl2_recreate_fbo() gave the larger texture no filter, so it kept GL's
default GL_NEAREST_MIPMAP_LINEAR with a single level. On GLES that
texture is incomplete, and once a viewport-scaled pass grew, the next
pass sampled black; desktop GL with texture storage gets a complete
one-level texture. Make it with gl2_create_fbo_texture(), as at init, so
it also keeps the pass's filter, wrap mode and float or sRGB format. The
feedback texture is regrown the same way.
* video: per-screen window settings and the screens' window plan
Each screen gets window settings in the config,
video_screenN_window_mode, _x, _y, _width, _height, _monitor and
_fullscreen: Attached, Separate or Hidden, and where a window of its own
goes and how big it is. Screen 1 is always Attached; the others default
to Separate. video_windows_enable, off by default, is the switch that
lets screens leave the main window at all.
video_windows_plan() turns those settings into a frame's plan: the
screens the main window draws, the ones that go to windows of their own
and the hidden ones. A screen stays in the main window while the switch
is off, where the driver has no windows, or once the renderer has sent
it back. The views layout takes exclude_screens, so the main window lays
out only the screens it keeps, and a window only its own.
This is the base the desktop secondary windows and the Android second
display share. The video_windows harness, run in the Linux gfx samples
workflow, checks the plan and a layout with screens left out.
* video: contract for secondary screens' native windows
A context can offer windows of their own for secondary core screens
through an optional gfx_ctx_window_driver_t, the last member of
gfx_ctx_driver_t: create, update, poll and destroy, and bind and
present for GL contexts. Requests, native handles and events carry a
generation from video_driver_window_generation(), a frontend counter
that a driver or context restart does not reset, so an event that
outlives its window cannot be taken for its replacement's.
GFX_CTX_FLAGS_VIDEO_WINDOWS is what a video driver will report once it
draws to such windows; nothing sets it yet.
* egl: window surfaces for secondary screens
egl_window_surface_new/bind/swap/free give a secondary screen's window
a surface on the main context's display and config, with the main
window's attributes. Bind makes the context current on it at swap
interval 0, or on the main surface again; free puts the main surface
back first where the freed one is current. None of them log.
* video: share the secondary windows' event bookkeeping
What a native layer keeps of each secondary window for the frontend -
its owner, record, request, position, the events waiting one bit per
kind, and whether it closed - is a video_window_common_t at the start
of its own state, and the rules on it live once in gfx/video_windows.c:
marking a window closed (CLOSE, with FOCUS_LOSS where it had the focus,
a waiting gain or resume dropped), poll's drain in its fixed order with
whatever has no room left waiting, and the sweep of an owner's windows
at context teardown.
* video: one placement clamp for secondary windows
video_window_place_rect() puts saved bounds on a monitor or its work
area: bounds partly on it are moved onto it, automatic ones and those
not on it at all are centred, and the size is clamped. Whether bounds
are on it is judged as saved, before the size is clamped, so an
oversized window partly on it keeps its position on the other axis.
* video: frame snapshot for secondary windows
A frame presented with views now carries what a driver needs to make,
place and lay out the screens' own windows without reading settings:
each screen's window settings, the window plan, the core's own map and
frame size, the layout parameters and a restore count, from which
video_driver_window_layout() lays out one screen's window. The snapshot
lives in video_driver_state_t and video_frame_info_t points at it, NULL
while windows are off or the driver cannot make them; the threaded
wrapper copies it into the frame's slot, as it does the statistics text.
While the driver can make windows, the main window's layout leaves out
the screens that have one of their own or are hidden.
video_driver_restore_windows() clears the screens a close attached and
counts the request, and window generations can be taken on any thread.
* video: published layouts and translate for secondary windows
A renderer publishes each secondary window's layout as drawn, with the
core map it was made from and the window's client size, through
video_driver_publish_window_layout() from the thread that runs the
context. Latest wins with no lock: each window has three slots handed
over by index exchange, as the window title is, and NULL clears it.
video_driver_translate_window_point() maps a point in a window to the
core's frame: window 0 through the viewport, as before, and a secondary
through its published layout, scaled from client to drawn pixels. A
point in a window that has no layout, was cleared or has another
generation is not mapped.
* input: window-aware pointer and touch publication
input_driver_publish_window_pointers() and _touches() take each device's
and each touch's window: 0 for the main window, else a screen's own with
its generation. A device in a screen's window is placed through that
window's layout for the pointer and the lightgun, and has no place on
the main window's screen; one whose window is gone reads off-screen.
Relative motion, buttons and the ports' devices are as they were.
input_driver_publish_pointers() and _touches() publish into the main
window, so the input drivers that call them need no change.
* samples: video_screens harness for the screens' own windows
The shared windows base is tested against the shipping configuration,
video and input objects: per-screen window settings across saves and
overrides, a screen's own layout, the frame snapshot and its copy
under the threaded wrapper, published layouts and point translation,
window-aware pointers and touches, and window generations. build.py
links the test with a configured non-Qt build's objects; the OpenXR
desktop jobs, which make one, run it.
* video: the window plan can keep a screen other than 0 attached
video_windows_plan() takes the screen that stays in the main window
whatever its mode, and screen 0 takes that screen's mode. Callers pass
0, which plans as before. Android's external monitor rule needs it:
screen 0 goes to the monitor and screen 1 stays on the device, where
the touch screen is.
* video: route the separate screens to a platform's displays
video_windows_route() chooses the display a platform's Separate
screens go to, from a list the platform publishes. A screen's monitor
setting N names the display of ordinal N; 0 is Auto, which takes an
on, valid and physical display that is neither private nor the main
window's: an external one (DIRECT or TRANSITIVE) first, then the
lowest ordinal. On an external monitor Auto puts screen 0 there and
keeps screen 1, so the touch screen stays on the device. One display
takes screens at a time; a screen bound for another stays attached.
Each display also carries its size, for the menu's display picker.
* samples: a touch through a second output's layout
A touch on a second display's surface, mapped through the layout drawn
there, lands in the touched screen's part of the packed frame: one
screen on a 1240x1080 panel, two screens sharing it, and screen 0 on a
monitor; the letterbox reads off-screen when asked.
* samples: pin the attached screen, picker and pillar cases
The plan keeps a screen other than 1 attached with screen 0 in its
mode, and falls back to Attached with no settings. The picker refuses a
private display but takes one that is off, and Auto never takes an
entry without an ordinal. A touch in a pillar beside two screens, not
reported off-screen, is held to the nearer one.
* video: route the screens' windows through a platform's display list
A platform publishes its displays from its UI thread, latest wins,
through three slots as the window layouts are handed over. Each frame
takes the newest list, routes the Separate screens with
video_windows_route() and plans with the screen it keeps attached; a
screen with nowhere to go stays in the main window. All screens bound
for one display share one window, led by the lowest one, and the frame
says which screens each window shows. Taking a list restores the screens
a failed window sent back, and video_driver_attach_windows() lets a
renderer send them back from any thread. A request names its display,
and Android gets a native window kind.
A screenshot or GPU recording through the threaded wrapper draws the
cached frame again on the video thread. That redraw takes no list: it
keeps the app thread's last route, as it reads the forced and restore
state, so a renderer keeps the second display's window and the capture
shows the right screens. The route is written only when it changes, so
Win32, which routes nothing and whose modal redraw runs beside the app
thread, never writes it while it is read.
The video_screens harness redraws the cached frame through the real
threaded wrapper and checks that it keeps the frame's route and plan.
* video: let a platform take the secondary windows back
video_driver_windows_retire() has the renderer let go of every
secondary window, through a new optional poke hook. It is for a
platform command such as Android's second surface going away, never a
frame. With the threaded wrapper up it is queued to the video thread
behind the frame being drawn, and nothing waits for it: a hook the
caller passes runs there just before and just after, for the platform
to hold its own requests back and to hear when it is done. Without the
wrapper all of it runs before the call returns. One is queued at a
time; false says the video thread cannot take it yet.
video_screens checks that a retire queued behind a frame being drawn
returns at once, and runs after the frame, between the hook's calls.
* video: one slot hand-over for the window layouts and displays
The window layouts and the display list are handed over the same way,
through three slots. Two small helpers publish and take a slot for both,
with the same memory orders. No behaviour change.
The harness adds two display list checks: a longer list keeps its first
VIDEO_WINDOW_DISPLAYS_MAX, and a get of capacity 1 fills only the first
entry.
* android: native side of the second display
The Java controller hands native code the display list and the second
display's surface through three natives. The list goes to
video_driver_publish_window_displays(). A surface becomes a node holding
one ANativeWindow reference, exchanged in for the video thread to take,
with a generation the touches carry. A size change of the same window
keeps the generation; a different window first gives up the old one.
Giving one up uses the main window's ticket handoff: the UI thread waits
while the app thread drops the node and queues the renderer's letting go
of its windows. The thread that lets go of them acknowledges once it is
done, so the UI thread is not left waiting for the app thread's next
poll; a retire the video thread cannot take yet is tried again from the
input poll. Native code asks Java to show or hide the display through
setSecondDisplay(), which posts and never waits, and asks nothing while
the renderer is letting go of a surface. The app state gets a 64-record
ring for the display's touches.
The natives run on the UI thread and read its own app state,
g_android_early, which android_app_free() clears unless a re-created
activity has already replaced it, so a callback after onDestroy finds
none. A display name GetStringUTFChars cannot read leaves no exception
pending for the next JNI call.
* android: second display windows for the gl and vulkan contexts
Both Android contexts give the renderer the secondary-window table.
Creating a window asks Java to show the display it is routed to and
returns unconfigured. Until Java publishes its first display list a
Separate screen resolves to display 0, which names no display, so its
window is refused and the renderer keeps the screen attached until a
list arrives. An update takes only the request's settings; another
screen or request generation is a new window, the renderer's to make.
Its poll hands it the newest surface native code received, raising
GEOMETRY and publishing which window and surface the input poll maps
touches through. The EGL context makes the window's surface on the main
context the first time it is bound after a surface arrives, at swap
interval 0, with the native visual id queried once at init, since the
query logs on failure and binding sits on the present path; the Vulkan
context gives the renderer the ANativeWindow and leaves the surface to
it.
Each request native code sends Java carries a number, and each surface
comes back with the number of the request it was made for. Destroying
the window drops the surface it held, then asks Java to hide the
display. When it was the last window, that hide gives up every surface
made for an earlier request, including one that reaches native code
after it: no window takes one, and its surfaceDestroyed frees what is
left and returns without the ticket handoff. A hide native code starts,
for closing or switching content or reinitialising the video driver,
never holds the UI thread.
* android: touches on the second display reach the core
Each touch on the second display crosses from the UI thread in the ring
with the generation of the surface it was on. nativeTouch sits with the
other second display natives and reads the UI thread's own app state,
which android_app_free() clears. A down or a move goes in only when it
leaves room for an up of every finger the input driver tracks, and an up
only when its down went in, so a full ring never drops a lift and leaves
a finger pressed for the core. MAX_TOUCH moves to platform_unix.h for
the native and the input driver to share.
The input poll drops the touches of any surface but the one the renderer
shows, maps the rest through that window's published layout into the
core's frame, and keeps the fingers after the main display's in the
core's pointer list. They never move the mouse, count as a quick tap or
reach the frontend's own screen pointer, so the menu and overlay stay on
the main display.
* input: the second display's fingers on a device of their own
With an overlay's pointer on, or one of its buttons held, the frontend
answers RETRO_DEVICE_POINTER from the overlay or not at all, and a
windowed overlay polls that device as its own touches. So the second
display's fingers cannot share it: the Android driver answers them on a
frontend-only RARCH_DEVICE_POINTER_SECOND and keeps RETRO_DEVICE_POINTER
to the main display. The frontend lists them for the core after the main
list's pointers, whoever answers for those: the overlay, none while the
pointer is blocked, or the driver. A driver with no second list answers
with its main list alone.
The pointer_second harness checks those answers against a stand-in
driver with both lists, through input_state_device() and the overlay's
pointer answer extracted from input_driver.c.
* android: a Presentation for the second display
SecondDisplayController lists the displays for native code - whether
each is the Activity's own, valid, on, private, a real panel or monitor
(product info, API 31+), its connection, name and size - and shows a
Presentation with a SurfaceView on the display native code asks for. The
window is not focusable and not touch modal and the view is not
focusable, so touching it neither pauses the core nor takes the gamepad;
it keeps the screen on and, on API 29+, keeps system gestures off it. It
follows the Activity from onStart to onStop, holding a request made
while stopped, and a frame after a display or configuration change
publishes the list again when it changed. A display it cannot show on
stays invalid until the list changes, so the display events a
Presentation coming or going may fire do not retry the failing show in a
loop. Each Presentation carries the number of the show request it was
made for and hands its surfaces to native code with it; touches go with
the surface's generation.
* android: second display failures and Presentations Android dismisses
Every display a show failed on stays invalid until the list changes or
the Activity starts again. A single failed id would not do: of two
displays that both refuse a Presentation, marking one failed makes the
other valid again, native code routes back to it, and the two take turns
failing forever. Starting again retries a one-off failure when the app
returns.
Android cancels a Presentation itself when its display's metrics change
(API 17-29). A dismiss we did not make clears it, so the dead
Presentation does not leave its display counted as shown. While native
code still wants that display, it is shown on again if the dismissed
Presentation's view had a surface. One cancelled before that cancels
itself, for example on stale display metrics, and rebuilding it would
loop at message rate; it counts as a failed show, so its display is
published invalid until the list changes or the Activity starts again.
* video: which screen windows a renderer makes and lets go each frame
video_windows_steps() tells a renderer, from one frame's snapshot,
which of its windows to let go of (no screens now, or another
display) and which to make. A window that closed or failed is not
made again until the restore count moves, so frames already queued
do not bring it straight back. Renderers share the rule instead of
each keeping its own.
* gl2: views draw into a given output; the preset's targets can grow
gl2_views_render() takes the layout, size and canvas it draws with
instead of reading the main window's, and the loop that grows the
preset's targets on a resize becomes a function of its own. Nothing
changes for the main window; a screen's own window will draw through
both.
* gl2: draw the screens' own windows
With a context that can make windows of their own for screens, gl2
reports GFX_CTX_FLAGS_VIDEO_WINDOWS while it may draw views. Each frame
it makes and lets go of the windows the frame's snapshot asks for and
draws each one's screens through the views compositor, with the preset's
passes where one is loaded. A window's pass targets grow only once its
surface is bound, so a failed bind has nothing to undo. A window is
drawn after the main window's frame and swapped at interval 0 before the
main swap, which stays the only one that can wait. The menu, OSD,
widgets and overlay stay in the main window.
A window that cannot be made or drawn hands its screens back to the main
window until the next restore and logs once. A window its context closes
hands them back too, and a restore that lands in the same frame does not
make it again; one closed with no screens left is not marked failed, so
it can be made again when its screens come back before the next restore.
The poke's windows_retire lets go of every window at once, for a
platform that takes a surface back.
The gl2_pbo_readback and gl_last_pass_fbo samples include gl2.c; neither
draws windows, so their shared stubs answer the frontend's window calls
with no-ops.
* samples: views test core logs a second finger, and asks for GLES 3
With a finger on each of two displays the second display's finger is
pointer 1. The core reads, maps and logs pointers 0 and 1 through one
helper, each with its own last state; pointer 0's lines and crosshair
are unchanged. Android's gl driver only gives GLES contexts, so a gles3
value of video_views_test_hw lets the core render through hardware there
too. gles3 with the checker or noise pattern logs an error at load:
those patterns upload a BGRA texture, which GLES 3.0 does not take.
* vulkan: the window-system surface switch as a helper
vulkan_surface_create() made the VkSurfaceKHR for each window system
in line. The switch moves into vulkan_wsi_surface_create(), which
takes the instance and returns the surface, so a second output on the
same device can make its own surface the same way. VULKAN_WSI_DISPLAY
stays where it was, since it needs the context's GPU. No behaviour
change.
* vulkan: a second output on the context's device
A vulkan_output_t is a surface of its own with a swapchain on the
context's device and queue, for a screen shown on another display. It
never waits in a frame. The acquire takes no time and uses a fence,
never a semaphore, so nothing the second display's presentation engine
signals ever enters the queue the main output's frames share. The fence
is polled, and an image not yet free is kept for the next frame. Out of
date remakes the swapchain at once; suboptimal remakes it once and then
keeps it, as the main chain does. On Android the swapchain takes the
size the window reports, since the surface's currentExtent can lag an
in-place resize and Android scales any extent to the window. A lost
surface is dropped. A swapchain let go of is destroyed once a fence
queued behind its last present has signalled. Only teardown waits, on
the output's own fences.
The harness runs the real code against a fake device and checks that no
frame-path call waits, that only completed objects are destroyed, and
that everything made is freed.
* vulkan: draw screens in windows of their own
Where the context has a windows table (Android's second display), a
screen the plan sends to a window of its own is drawn there with its
views' chains, sized for the window, and the frame's own view copies.
The window is drawn in the frame's command buffer, after the main pass
and before the chains end their frame. Its image comes from a fence
already signalled, so the frame's submission waits on nothing new; it
signals the window's semaphore, and the window presents after the
submission and before the main present, which stays the only one that
waits. The menu, OSD, widgets and overlay stay in the main window.
A window that can't be made, drawn or kept puts its screens back in
the main window until the next restore. One whose output is not done
yet is let go of on a later frame; the poke's windows_retire and the
driver's teardown wait for it, on the output's own fences.
* video: separate screen windows on by default on Android
On Android a Separate screen goes to a second display, which is what a
device with two panels is for, so the switch starts on there. Desktop
windows stay opt-in. The settings table records the Android default;
the harness checks that an override carries the switch and a screen's
mode.
* menu: a window mode and a display for each screen from 2 on
Each screen past the first gets two settings rows: Attached, Separate or
Hidden, and the display it goes to when it is Separate. The display
picker offers Auto and the displays the platform has published, shown as
the monitor index is ("2 (Screen-2 1240x1080)"), and keeps a display set
elsewhere until it is changed. It widens only up to the most displays
that can be published, so a hand-edited value in the thousands cannot
build a list that long on the main thread. The row's sublabel says that
the display RetroArch is on keeps the screen attached. Each screen's
rows have their own enum, as the per-port input rows do, so the dropdown
and the sublabel find the right one. A change to any of the rows gives a
screen the renderer sent back to the main window another try.
The settings harness checks the rows, the left key's clamp, and that a
change to any of them restores closed windows.
* menu: show the screens' window rows where the driver has windows
Stereo 3D & Screens shows Separate Screen Windows when the video
driver reports secondary windows. While it is on, it also shows the
mode and display of screen 2 and of each further screen the core has.
On Android the switch reads "Show secondary screens on another
display". A screen row's dropdown takes that row's label as its
title.
* android: show the second display again after its metrics change
On Android 10, a density or size change on the second display left it
empty until the app restarted. Android cancels a Presentation when its
display's metrics change, and the one made to replace it got the
display's resources from a cache keyed by display id, still holding the
old metrics. Presentation.onStart() found them stale and cancelled it
before it drew, so the display was marked failed and both screens stayed
on the main display.
Bring the new Presentation's resources up to the display's current
metrics before showing it. The refresh runs only before Android 12; from
then on a Presentation has a window context of its own.
* android: read input queue keys and sticks through their fields
The key and stick helpers of the input queue take the few fields
they read instead of the event, and the default loop's slot lookup
and key path become functions of their own. Nothing changes for the
queue; the second display's window can then hand its keys and stick
moves to the same code.
* android: a focus rule for the Activity and the second display's window
Once the second display's window can take focus, RetroArch's focus
comes from two windows. Where each display has its own focus, the
second window keeps its display's focus while the keys go elsewhere,
so it counts only while in use: after a key, stick move or touch from
it, a focus it gained while the Activity had none, or a hand-off of
the one focus from the Activity. Any other change of the Activity's
focus ends that, so a notification shade still pauses as before.
The rule is a header without NDK types; a host test replays the
sequences the AYN Thor's focus modes and a one-focus system produce.
* android: keys and sticks from the second display's window
Keys and joystick moves the second display's window receives cross
from the UI thread in a ring of their fields; the input poll handles
them with the input queue's own code, so mapping, device ids, axes,
hats and hotplug behave the same. The window's focus comes in as two
commands, and the focus of both windows is acted on once per poll by
the focus rule, only when it changes.
A paused app thread sleeps in its looper, so writing to either of the
second display's rings wakes it, and the poll reads both rings after
the looper so the woken poll acts on what woke it. The app thread
keeps a reference on its looper for those wakes until the activity is
freed.
* android: the second display's window takes focus and passes keys on
The second display's Presentation can take focus, so a tap on it or a
focus mode that locks the gamepad to its display (the AYN Thor's Bottom)
gives it the pad. Its keys and joystick moves go to native input as the
Activity's do. Each key gets native code's answer, so Android makes no
fallback key (BUTTON_B as BACK, BUTTON_MODE as HOME) of a key RetroArch
uses. It is not cancelable, so BACK stays with native code, and it never
becomes the soft keyboard's target. The IME refuses the Activity's text
field while the second display's window holds the focus, so the Activity
asks for the soft keyboard again when it regains focus.
When RetroArch dismisses the Presentation while it holds the focus, it
first brings its own task to the front, which takes REORDER_TASKS
(granted at install). Android keeps one focus for all displays, so it
would otherwise pass to that display's launcher and leave RetroArch
without focus until the next tap on the main display.
* android: hand the second display's surface back without waiting
surfaceDestroyed and a window change on the second display return at
once. The renderer's own ANativeWindow references keep the window
alive; a frame still drawing into it gets an error from the abandoned
queue, and the next frame's poll finds the surface gone, drops it and
lets the EGL surface or Vulkan output go, the output on later frames
while the presentation engine still holds an image. Nothing waits on
the video thread or the GPU, so the UI thread can't stall into an ANR
behind a slow frame, and the window keeps its display for the next
surface. The going surface also gives the windows another try, so a
failure it caused is not held against the next.
vulkan_output_finish() is the driver's teardown only. An acquire it
gave up on keeps its fence, which is never destroyed while it may still
be signalled.
* video: build the screens' own windows only where a platform has them
HAVE_VIDEO_WINDOWS gates the secondary windows: their state in the
video driver and the threaded wrapper's slots, the drivers' window code
and its per-frame calls, the second output and EGL window surfaces,
the input layer's second pointer list and window places, and the
per-screen settings with their menu rows and config keys. Android
defines it; elsewhere ./configure --enable-video_windows does, and the
CI job that runs video_screens_test builds that way. A build without it
compiles none of this and carries none of its state, which is about
15 KB of the video driver's state alone.
* video: a window failure counts only for the restore it was drawn under
A renderer reports a window it could not make or keep with the restore
count of the frame it was drawing. Failures travel in one word, the
screens beside that count, and the main thread takes them only when
the count is still its own. Under threaded video a frame laid out
before a display or surface change can fail after that change; its
failure no longer keeps the screen on the main display until some
later event.
* android: the jelly-bean package may reorder tasks too
It builds the same second display controller, which brings RetroArch's
task back to the front when it dismisses the second display's window on
a one-focus system; without REORDER_TASKS that call throws and the focus
stays on that display's launcher.
---------
Co-authored-by: libretroadmin <[email protected]>
|
||
|
|
d6a23c320a |
video: simulated input resolution for slang shaders (#19675)
Shader settings gain Simulated Input Resolution with a width and height, off by default. While it is on, a slang preset's first pass is told its source has that size, so resolution-sensitive shaders such as CRT and edge-smoothing ones treat an upscaled frame as the small image they were written for. Later passes keep their real SourceSize. The size is recorded in the preset when it is loaded and every slang backend reads it from there: glcore, Vulkan, D3D10/11/12, Metal and gx2. Changing a setting reapplies the preset. The rows appear in the shader quick menu only when the video driver takes slang. slang_hlsl_backend checks that the size reaches the first pass alone and survives the copy the D3D drivers make of a pass's semantics, and runs in the gfx samples workflow. Co-authored-by: libretroadmin <[email protected]> Co-authored-by: hunterk <[email protected]> |
||
|
|
650cf882db |
Add shared Media Player settings below File Browser
Expose fullscreen seek interval and controls auto-hide preferences through shared menu navigation. Preserve the existing five-second seek and three-second hide defaults, keep zero-delay controls visible, and publish the configured seek interval for matching button captions. Exercise actual navigation, editing, persistence and kiosk visibility in all four menu drivers. |
||
|
|
b4204ab02b |
configuration: move label-keyed hand rows into the row tables
Hand-written bool and uint settings whose key is an unconditional menu label and which carry a default become rows of config_bool_rows and config_uint_rows, keyed by their label's blob offset like the generated rows. Rows with an override, a sensitive flag, no default or an #else branch stay as code. So do the float rows, whose table is only added with the menu, and the int rows, whose trailing override binds to the last row added. |
||
|
|
bf777848c6 |
Verify packed settings table keys
Resolve label offsets from preprocessed blob declarations instead of dropping generated settings rows. Refresh the reference for ten already-landed settings and microphone row ordering, with all existing keys and defaults preserved. Cover packed and literal keys, ordering and missing labels, and run table and unity checks in settings CI. |
||
|
|
c13b93c757 |
d3d9: Direct3D 9Ex Presentation, the flip model and frame latency
A new setting, Direct3D 9Ex Presentation (video_d3d9ex_present), off by default, has the D3D9 drivers on a Direct3D9Ex device present as D3D11 does. A window presents through the flip model (D3DSWAPEFFECT_FLIPEX), which the compositor takes as it is instead of copying; a device that will not present that way is made with the discard swap effect, as before. With V-Sync and Waitable Swapchains on, Max Frame Latency is honoured the way D3D11 takes it: 1 to 4 is the device's maximum frame latency, 0 is a latency of 1 with a wait for the display after Present, and -1 the wait before a Present that goes out at once, which only the flip model has (elsewhere it waits after). A device that cannot wait for the display (Wine's) keeps the latency and does not wait. With the setting on, the D3D9 drivers report a customizable frame latency, so Waitable Swapchains and Max Frame Latency show for them; the setting itself shows under the D3D9 drivers. Off, or without Ex, nothing changes: pacing users have calibrated around stays as it was. samples/gfx/d3d9_ex makes a device in the flip model, sets its latency through the Ex table d3d9_tex.h lays out and presents in each of d3d9_present's ways; swfb.py, with SWFB_D3D9EX_PRESENT, checks that a d3d9_hlsl window took the flip model and the latency, draws what D3D11 does, and that a device without Ex presents as before. |
||
|
|
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 |
||
|
|
885dc8da31 |
Recording/streaming: no more game slowdown, correct A/V timing, Twitch-ready presets (#19752)
* recording: add video_record_allow_frame_drop and video_record_fifo_frames
Wires two record_ffmpeg queue knobs through the settings_def
single-source tables, record_params and the Recording settings list
(shown for the ffmpeg driver only).
video_record_allow_frame_drop bool, default true
video_record_fifo_frames uint, default 32, 8..128 step 4
(Lakka: advanced only)
Both are read when a recording starts; the driver side is the next
patch. msg_hash_lbl_str.h regenerated with tools/gen_lbl_str.py, keeping
only the two new rows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: drop instead of stalling when the encoder falls behind
With the encoder behind, a full queue blocked the frontend in
push_video/push_audio, so a slow encoder slowed the game down.
With video_record_allow_frame_drop (default on):
- a frame that doesn't fit is dropped; the count rides with the next
queued frame and the encoder leaves a pts gap for it, so video stays
aligned with audio
- an audio chunk that doesn't fit is owed as silence and written ahead
of the next chunk that does, and anything still owed is encoded at
finalize, so the audio track keeps its length
- drops are reported every 64 frames and summarised at teardown
The video queue depth comes from video_record_fifo_frames (8..128).
The audio queue holds 2 s at the core's rate instead of ~0.36 s at
48 kHz. With drop off, both paths block as before.
ffmpeg_pipeline_test gains a drop-mode pass that checks the surviving
frames come out in order, pixel-exact and at their own timestamps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: make RTMP streaming presets ingest-compliant
The Twitch/YouTube/Facebook/Kick modes reused the recording presets:
CRF (unbounded VBR), the encoder's default GOP (~4 s for x264 at
60 fps), audio at whatever rate was closest to the core's and no
audio bitrate. Ingest servers want CBR H.264, a 2 s keyframe interval
and AAC at 44.1/48 kHz.
For the FLV/RTMP modes:
- CBR video at 2500/4500/6000 kbps (low/med/high), 1 s VBV buffer,
x264 nal-hrd=cbr, no B-frames
- a fixed 2 s keyframe interval: x264 scenecut=0, and keyframes are
forced on time rather than counted in frames, since pts gaps from
dropped frames or cores presenting every other tick stretch a
frame-counted GOP
- AAC 160 kbps at 48 kHz
Rate control goes through the generic AVCodecContext fields so it
carries over to other encoders. Local/custom streaming and the
recording presets are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: optional hardware H.264 encoder with libx264 fallback
New setting video_record_hw_encoder (default on for HAVE_LAKKA_SWITCH,
off elsewhere). When set and the built-in preset would use libx264,
these encoders are tried in order and the first that opens is used:
h264_nvv4l2, h264_nvmpi, h264_v4l2m2m, h264_rkmpp, h264_nvenc,
h264_amf, h264_qsv, h264_videotoolbox, h264_mediacodec
All take system-memory frames; VAAPI is skipped because it needs a
hw_frames_ctx upload path. Input is YUV420P if the encoder takes it,
else NV12 (via swscale). x264 private options are not passed to them;
rate control/GOP come from the generic AVCodecContext fields, plus
rc=cbr for the RTMP presets. CRF presets get a bitrate derived from
0.05/0.1/0.2 bits per pixel per frame (low/med/high), floor 500 kbps.
If none open, libx264 is used as before. Custom configs are untouched.
ffmpeg_init_video is split so one encoder attempt is a self-contained
alloc/configure/open that frees its context on failure. Also sets
AVCodecContext.framerate, which some hardware rate controllers need.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: run encoder threads at lower priority in drop mode
With a CPU-bound software core, libx264's worker threads, the
swscale/AAC work on the encoder thread and the core's main thread all
compete at the same priority, so recording slows the game even when
the queues never fill.
In drop-on-full mode, run everything encoder-side at nice +10:
- the driver's encoder thread renices itself on start
- threads spawned during the video/audio avcodec_open2() (x264's pool
and lookahead) are found by diffing /proc/self/task and reniced
afterwards; lowering the main thread around the open instead isn't
reversible without CAP_SYS_NICE
The encoder then gets what the core leaves, and any shortfall shows up
as dropped frames rather than game slowdown. Linux only; skipped when
drop-on-full is off, since starving the encoder there means stalls.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: route libav* logging into RetroArch's log
FFmpeg logged to stderr through its default callback, so codec
messages never reached RetroArch's log or log file, which is what a
user can attach after a crash.
While recording, install an av_log callback that assembles lines
FFmpeg builds from several calls and forwards them as
RARCH_ERR/WARN/LOG/DBG by level, tagged [FFmpeg/lav]. A partial line
is flushed when the next piece comes from another context, so two
threads' messages are never spliced. FFmpeg's level follows the
frontend log level. The default callback is restored when the
recording is freed.
Adds verbosity_get_log_level().
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* video: BGRX readback for GPU recording (gl, vulkan)
GPU (post-shaded) recording, which hardware-rendered cores are forced
into, read every frame with read_viewport(), which converts the
32-bit readback to BGR24 on the main thread inside the frame budget.
The encoder thread then converts BGR24 to YUV again, and the frame is
queued at the viewport rounded up to powers of two.
- Add an optional video_driver_t::read_viewport_bgrx() that returns
the viewport as 32-bit B,G,R,X plus its row order. It is the last
slot, so positional driver tables leave it NULL.
- gl: desktop GL already reads back GL_BGRA, so rows are copied as-is
(RGBA is swizzled on GLES), on both the PBO and synchronous paths.
- vulkan: B8G8R8A8 swapchains copy rows as-is, RGBA ones swizzle.
- Recording uses it when the driver has it, feeding the encoder
ARGB8888: the main thread's work per frame becomes a plain copy and
the encoder thread converts once. Little-endian hosts only, since
FFEMU_PIX_ARGB8888 is a native-endian word. The threaded wrapper
keeps its own readback on the video thread.
- The encoder queue is sized to the exact viewport (a resize already
ends the recording): 1280x720 rather than 2048x1024 per frame.
The BGR24 path is unchanged for screenshots and other drivers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* video: BGRX readback for glcore, gl1, sdl2 and sdl3; fix sdl2 readback
Extend read_viewport_bgrx() to the other Linux-capable drivers.
- glcore: the async PBO readback reads GL_BGRA on desktop GL (GLES
keeps RGBA), with the BGR24 scaler's input format to match, so
recording gets a plain row copy as on gl.
- gl1: synchronous RGBA readback, swizzled to BGRX.
- sdl3: sdl3_capture_viewport() can emit BGRX, top row first.
- sdl2: read_viewport() converted the whole window surface
(SDL_GetWindowSurface() on a window that has a renderer), copied
surface->h * pitch into a buffer sized for the viewport, top row
first where callers expect bottom row first, and leaked the
converted surface on every call, so GPU recording and screenshots
came out as garbage. Do what sdl3 does: redraw the last core frame
into the game viewport and SDL_RenderReadPixels() exactly that, as
BGRX or as flipped BGR24.
The drivers that pass OpenXR slots positionally get NULLs for them
so the new slot lands in place.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* recording: performance counters for the recording path
Register frontend performance counters for each stage of a recording,
reported at exit with perfcnt_enable, so its cost can be placed:
record_readback main thread: GPU readback
record_push_video main thread: queueing a frame for the encoder
record_push_audio audio path: queueing audio
record_scale encoder thread: colour conversion and scaling
record_encode_video encoder thread: video encode and mux
record_encode_audio encoder thread: audio resample, encode and mux
Counters are only touched with perfcnt_enable set, and each has a
single writer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: timestamp video from the audio clock
Video pts was one tick per frame handed to the encoder, i.e. it
assumed the core delivers a frame on every retro_run(). Many don't: a
30 fps game on a 60 Hz core (Dolphin's Wind Waker, for one) only
calls the video callback for new frames, and frame skipping and
run-ahead do the same. Those recordings played their video at the
wrong speed: a 26-minute Wind Waker session produced 13 minutes of
video under 26 minutes of audio, and RetroArch's own player, which is
clocked by audio, hung once the video track ran out.
Count every audio frame the core produced, which is the core's real
elapsed time, and place each video frame at that point on the
timeline through the same pts skip dropped frames use. Frames that
arrive every other tick get pts 0, 2, 4, ... Without audio, the
one-tick-per-frame accounting is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* recording: optional "Record Game Only" (no menu, OSD or menu time)
GPU recording reads the backbuffer at the end of the frame, so the
volume and other OSD messages, notification widgets, statistics,
input overlays and, with content running behind it, the menu end up
in the recording, and the menu's filler silence puts the time spent
in the menu into it too. That is what's on screen, which some users
expect, so this adds a switch rather than changing the default.
New setting video_record_game_only ("Record Game Only", default off).
It travels with the frame, like gpu_recording. When on:
- gl, glcore: the async PBO readback is issued right after the core's
image (shader chain, PQ->SDR conversion) is in the backbuffer,
before any UI is drawn. Under scRGB output the backbuffer only gets
the image at the final composite, so that path reads back at the end.
- vulkan: after the final pass, end the render pass, copy the
backbuffer to the readback staging buffer and resume with
keep_render_pass (LOAD), so the UI draws over the unchanged image.
HDR output keeps the end-of-frame readback, which needs its tonemap.
- audio_driver_menu_sample() doesn't push the menu's filler silence,
so menu time is left out, as its frames already are.
When off, behaviour is as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* recording: apply the content rotation to raw (non-GPU) recordings
Raw recordings encoded the core's frames as handed over, ignoring the
rotation the display applies (core-requested or the user's video
rotation), so vertical games came out sideways. GPU recordings were
unaffected since they read back the already-rotated screen.
Pass the rotation to the recorder, describe the output with width and
height swapped for quarter turns (unless --size gave one), and rotate
each frame on the encoder thread before scaling.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: scale frames smaller than the output size to fill it
ffmpeg_scale_input() clamped the scaling destination to the source
size whenever a frame was smaller than the output. That was meant for
the one pixel of padding added to reach even dimensions, but applied
to any smaller frame: frames below the core's base geometry landed in
the top-left corner of a black frame (Mupen64Plus-Next with Angrylion
and GPU recording off), and video_record_scale_factor > 1 had no
effect, since every frame is smaller than the scaled output.
Scale every frame to the output size; only a frame exactly one pixel
short of it in a dimension (the even-size padding) is copied 1:1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: fill audio gaps when the core stops producing audio
A core that produces no audio for a while, rather than producing
silence, sent nothing to the recorder, so that time was missing from
the audio track and everything after it was out of sync (e.g.
Mupen64Plus' 64DD loading). With video timestamps taken from the
audio clock it would also stall the video timeline.
Once 4 video frames in a row arrive without any audio, owe one frame
period (sample_rate / fps, with the fraction carried) of silence per
video frame, backfilling the 4 already seen, through the existing
owed-silence mechanism. The silence goes ahead of later audio in both
drop and blocking mode.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* record_ffmpeg: drop the global 'ctx', close the output on failure
The muxer context also lived in a non-static global named 'ctx', an
exported symbol in every binary that links this driver, used by
finalize to close the output. Use handle->muxer.ctx throughout.
A recording that fails after the output was opened (an encoder that
won't open, say) never reached finalize, so its file stayed open.
ffmpeg_free() closes it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tpe3orhELpViSQpZcsTTjr
* recording: rotated raw aspect, quiet drop mode, lowered encoders by inheritance, frame format across a threading switch
- A raw recording of a turned picture takes the display's aspect as it
is, which is already the turned one; only an aspect worked out from
the output size follows the turn, and --size keeps its own shape
(record_raw_geometry).
- Drop mode says nothing while it records: dropped frames are counted
and the total is logged when the recording ends.
- In drop mode the encoders are opened on a thread that has lowered its
own priority, so the threads they start run lowered and no other
thread is touched.
- Only the frontend-thread perf counters are registered, as the menu
reads and resets them live.
- A GPU recording opened for 32-bit frames gets them widened from BGR24
when the readback in charge hands over BGR24 - the threaded wrapper's,
or a driver without the 32-bit one - into its own buffer.
- At the end of a recording the real audio left in the queue is encoded
ahead of the silence still owed; the gap fill counts every frame the
core presents, frame_drop_ratio or not; the output is closed whenever
it is open; an encoder that leaves the CBR request unused is named.
ffmpeg_pipeline_test checks the geometry, that drop mode drops frames
without a warning or an on-screen message, and that its encoder
threads run lowered. threaded_video's gpu record format lane records
on Vulkan unthreaded and threaded with a 32-bit recording open, and
reads every row of what it is handed.
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: LibretroAdmin <[email protected]>
|
||
|
|
b9544ea208 |
OpenXR headset output for Vulkan on desktop Linux, (stacked on #19728) (#19729)
* gfx/video_views: check, lay out and map touch for a core's views The rules behind a view map live in one module with no driver or settings state: which maps are valid, how a frame is fitted to them, where each view goes in every stereo mode and screen layout, which part of a placement is drawn, and how a window point maps back into the core's packed frame or into the coordinates a per-eye UI is laid out in. Drivers and input share them, and a harness tests them without a GPU, under the sanitizers in CI. * menu: Stereo 3D & Screens settings Stereo Mode (2D, side by side half and full, top-bottom, anaglyph, interlaced), Swap Eyes and Screen Layout, under Settings > Video. They apply only to cores that send a view map. * video: accept a core's view map and hand drivers a layout The map is checked when the core sends it. Each frame the main thread lays it out for the current settings and passes the layout to the driver in video_frame_info_t, so drivers never read settings, threaded or not. A driver that can present views says so with GFX_CTX_FLAGS_VIDEO_VIEWS; otherwise, or when a frame doesn't fit the map, the packed frame is shown as before. A driver that draws a frame with a map whole anyway reports it, and the menu, overlays and touch follow the packed frame from the next frame. Under threaded video, a cached frame larger than the core's declared maximum, redrawn for a screenshot or a Win32 window drag, is now cropped as it was shown, so it makes the same views decision at the same viewport. The drag's redraw runs on the video thread beside the main one, so it lays out what was last presented and writes none of the views state. A hardware frame is no longer cropped: nothing is copied out of the core's image, and a cropped size dropped its views. View chains are built only through a new set_view_count poke, called when the accepted map or the driver's answer changes. Under threaded video the main thread waits while the video thread runs it, so preset parsing, which reads settings and replaces the file-watch list, never runs beside the main thread. A single frame that doesn't fit keeps the chains. Touch maps back through the layout into the core's packed frame, and a CPU filter's output scales the map without changing each view's shape. In side by side and top-bottom the menu and widgets are laid out at one eye's size. The menu maps its mouse and touch position through the layout, so either eye's copy is hit where it is drawn, and RGUI converts against the viewport its texture is fitted to rather than the whole window the driver reports while views are presented; the on-screen keyboard is hit-tested at the UI's size. A paused core asks for the views status only when it runs, so while the menu is open a change of the status (Stereo Mode, a headset) asks the core to update which of its options show, as an option change does. * input: overlays follow views Side by side and top-bottom hide the overlay, so its hit test is skipped there, while touches still reach the core, including through pointer passthrough. Elsewhere a non-fullscreen overlay is drawn over the whole window while views are presented, so it is hit-tested in window coordinates. * vulkan: draw a core's views Each view is copied out of the frame, shaded by its own chain with its own OriginalAspect, and drawn in its placement. A view's chain starts its passes and history small and follows the view's size, not the frame's declared maximum, which for a core like Azahar is 8192x8192 per buffer. A view shown in both eyes draws its final pass again with the first draw's source, so its feedback swaps once. Anaglyph and interlaced draw both eyes into an offscreen canvas and blend its halves into the window. A placement larger than its eye's area, as a custom viewport can make it, is cut there by a scissor on the chain's final pass. In side by side and top-bottom the UI is drawn once into a layer at one eye's size and blended into both halves, at screen depth, and only while there is UI to draw. The layer is FP16, drawn with the SDR pipelines HDR output already builds; with HDR off their alpha accumulates as coverage, so the layer composites as the UI drawn directly would. Views are off with HDR output or an HDR10 source. The driver publishes that answer for the main thread rather than reading its context flags, which the video thread rewrites each frame. A view that can't be copied or given a target falls back to the packed frame, and reports it. * glcore: draw a core's views Each view is blitted out of the frame's texture, keeping its orientation and swizzle, shaded by its own chain and drawn in its placement, following the same rules as Vulkan. Anaglyph and interlaced blend the halves of a canvas twice the window's width. GL counts rows from the bottom, so for interlaced with an even height the eyes take the other halves, keeping the left eye on the window's even rows. The blend programs are compiled on first use; without them the left eye is drawn alone. The UI is drawn per eye as in Vulkan, RGUI keeping its aspect inside each eye. Views need the slang chain, and are off with HDR output or an HDR10 source; get_flags reads published state only. * samples: a test core and end-to-end check for video views The core draws solid-colour views in the 3DS, DS and Virtual Boy shapes, in software, through a GL core context with either origin, or into Vulkan images of its own handed over with set_image, plus a frame larger than its declared maximum, which threaded video crops unless it is a hardware frame. It logs the views status, also when asked to update its options' display, and the touches it receives. run.py drives it in headless gamescope on vulkan and glcore through every stereo mode and screen layout, and checks screenshot colours and the logged status. One case runs a history preset over a core that declares a large maximum and checks the GPU memory it holds. One case runs on gl, which doesn't present views. The pointer cases move the pointer of gamescope's own X server from a helper inside the session and check that the menu's cursor, ozone's and RGUI's, is drawn under it in each eye's copy. It refuses to run on the desktop display. * vulkan, input: headset output through OpenXR PC headsets through the OpenXR runtime: a session on the Vulkan instance and device the runtime asks for, per-eye quad layers composed from the core's view map, flat-screen stereo modes, laser and menu pointer input, and the Monado end-to-end test infrastructure. The runtime is reached through the loader built into the tree, not a libopenxr_loader.so.1 opened at run time, so the same path works wherever the loader does, and the OpenXR headers come from the vendored SDK. The Vulkan instance and device wrappers route either owner of the context, this session or the Android projection context, with no cost where neither is built. OpenXR is on by default on Linux with Vulkan and threads, and its C is in the C89 lane like the rest of the tree. * vulkan: one swapchain per eye for the headset's stereo screens SteamVR on the Steam Frame ignores a quad layer's imageArrayIndex and shows array layer 0, so a stereo screen showed the left eye's image in both eyes and Swap Eyes changed nothing. Each layer of a slot now has a swapchain of its own, and each eye's quad shows layer 0 of its own. A slot's swapchains are acquired and released together, and its quads show once every one of them has released an image. The test layer can show layer 0 of every quad as the Frame does, and the e2e suite runs the stereo and Swap Eyes cases that way too. Revert this once SteamVR honours imageArrayIndex (ValveSoftware/SteamVR-for-Linux#975), to go back to one array swapchain per stereo screen. * openxr: the desktop headset path is built and checked in CI A job builds RetroArch on Linux with OpenXR on - the loader is in the tree, so nothing is installed for it - and runs the Stereo 3D & Screens menu harness, the API layer the Monado end-to-end check records through, and the video_views core, all with the vendored headers. The test layer no longer looks for installed OpenXR headers. * vulkan: the PC headset session is not opened beside the Android projection context * qb: OpenXR stays out of the C89 lane like the Vulkan it needs The vendored Khronos headers are not C89; the tree's own OpenXR C is. * video thread: the poke table keeps its place for the present wait * vulkan: the headset session's handoffs are lock-free What the XR thread tracks for the video thread (the predicted time, the anchor, pixels per radian) and the quads the video thread publishes for the XR thread and the pointer are each a seqlock over their words, the way video_driver.c publishes its viewport parameters, and the frame tick the video thread paces on is an eventcount. Nothing waits on a lock on either side; slot creation and destruction already happen under the queue lock. --------- Co-authored-by: libretroadmin <[email protected]> |
||
|
|
0f445b547d |
tools: settings table reference follows the Scanline Sync row move
video_scanline_sync is emitted where the Scanline Sync refactor placed it in the settings definitions. |
||
|
|
501990e6c1 | Scanline Sync refactor part 2 (#19710) | ||
|
|
40c3047b4e |
thread elevation: MMCSS on Windows, video and main thread priority
thread_elevation_raise_current() takes the kind of thread it raises. An audio thread goes through the backend chain, with the MMCSS Pro Audio class tried first on Windows. A Games thread - one that may run flat out - is scheduled ahead of ordinary threads but never real time: the MMCSS Games class on Windows, else THREAD_PRIORITY_HIGHEST; a lower nice value on Linux and Android; user-interactive QoS on Apple systems; nothing on the consoles. A token records what changed, and thread_elevation_lower_current() undoes it; each thread releases its registration when it ends. Threading gains Elevate Video Thread Priority, on by default, and Elevate Main Thread Priority, off by default and applied as soon as it changes. samples/frontend/thread_elevation holds that a Games raise never reaches the chain and that lowering puts the thread back. |
||
|
|
1e1a5a6e00 |
input: Full-Range Analog Triggers is on by default
A trigger that rests at one end of its axis is counted from where it rests: a full pull gives the whole analog range, and half a pull presses the button. With the setting off only the second half of the pull counted. It was added off, so that nothing changed for anyone. It is on by default now: it only acts on an axis bound to L2 or R2 that is seen resting at the far end from its bind, with nothing bound to its other direction, and for those the old reading was the wrong one. A trigger that rests in the middle and a combined axis with a trigger on each direction are left as they were. A configuration that has the setting saved keeps what it has. One case is worse off: L2 or R2 bound by hand to one direction of a stick, with nothing on the other. Pushing the stick all the way the other way makes it look like such a trigger. Turning the setting off puts that back. |
||
|
|
65331ad151 |
Network RetroPad: changes sent again, and First Sender Only
Two things for the controller sent over UDP. The Remote RetroPad core sends a control's state when it changes, and UDP may lose the message: a lost release left the control held at the receiver until it changed again. The core now sends a change again 5, 15 and 30 frames after it. Nothing more is sent while nothing changes, and an older receiver reads the repeats as it reads any message. New setting "Network RetroPad: First Sender Only" (network_remote_first_sender, off by default): each user's Network RetroPad keeps to the address of the first device that sends to it and ignores every other until RetroArch restarts, so a second device cannot take the controller over. It does not stop a forged address. samples/input/net_retropad_repeat drives the real core and reads its messages back over loopback, on Linux and under Wine; the frontend harness's network pad lane covers First Sender Only with a second loopback address. |
||
|
|
7bda2e938b |
input: Full-Range Analog Triggers
An axis bind takes one half of an axis. A trigger on an axis that rests at the bottom of its range, bound as "+N", therefore did nothing for the first half of its pull: its analog value covered only the second half, and its button came on at three quarters (issue #6920). New setting "Full-Range Analog Triggers" (input_trigger_full_range, off by default). With it on, an axis bound to L2 or R2 that is seen resting at the far end from its bind has its pull counted from there: a full pull is the whole analog range, half a pull presses the button. Which axes these are is learned as they are read, per controller, and forgotten when another controller takes the slot. An axis that rests in the middle is left alone, and so is one with a trigger on each direction, which rests in the middle too. Controllers read through the snapshot bridge only, which is every desktop joypad driver. The frontend harness gains a lane for it. |
||
|
|
5a467da4f8 |
input: Input Rotation, for the D-Pad and the sticks
New setting "Input Rotation" (input_rotation, OFF by default) turns the D-Pad and both analog sticks a core sees by quarter turns, for content shown rotated (issue #12796). The angle is counted as Video Rotation counts it: with the picture a quarter turn round, left on the controller is up in the game. 'Auto' follows the Video Rotation setting; it does not follow a rotation the core asks for, since a core that does so expects the picture to end up upright. The D-Pad is turned where a port's buttons are compiled for the frame, before SOCD cleaning, and a stick where its axis is asked for. Netplay and replay recording get the turned input. The menu is not affected. The frontend harness gains a lane for it. |
||
|
|
898e2a0fb2 |
input: SOCD cleaning for the D-Pad
Two settings, off by default, for what a core sees when opposite D-Pad directions are held together (issue #18345): - "SOCD Cleaning (Left + Right)": OFF, Neutral, Last Input Priority, First Input Priority. - "SOCD Cleaning (Up + Down)": the same, plus Up Priority. OFF passes both directions on, as before. Two directions that come down in the same frame are neutral under either priority mode. The cleaning is done where a port's RetroPad buttons are compiled for the frame, so it is one pass a port a frame, a core reading button by button and one reading the mask see the same, and netplay and replay recording get the cleaned state. It works on the buttons the core is given, after remaps. With a mode set, the port's buttons are compiled on the frame's first query, not its second. The frontend harness gains an SOCD lane. |
||
|
|
5d2e7b943a |
input: first press tells a key from a controller button
With "Assign Ports on First Button Press" on, a key bound to a port's controls assigned that port, and the notification then named the port's controller, which had not been touched. It also looked the controller up by the port's number, not by the controller the port reads. A port's keys and its controller are both that port's, as they always were, so a key still assigns the port by default. The notification now says "Keyboard assigned to core port N" for a key and names the right controller for a button. New setting "Keyboard on First Button Press" (input_assign_ports_keyboard): - "Assigns its Port" (default): as above. - "Waits for its Controller": a key assigns nothing while the port's controller is connected; a button of that controller does. With no controller on the port, the key still assigns it. The frontend harness's first-press lane covers both, with the keyboard stood in for. |
||
|
|
64451324e3 |
input: assign ports on first button press
New setting "Assign Ports on First Button Press" (input_assign_ports_on_button_press, off by default), after PR #18331. When content starts no controller is mapped to a core port; each is given the lowest free core port when a button is first pressed on it, and a notification says so. The press is looked for in the poll, only while the setting is on and some user has no core port. The d-pad does not count, and a button counts only once it has been seen released, so idle axes that read as a direction and stuck buttons assign nothing. The mapping itself is made after the frame's core run, not from the poll, and the core is then told the port's controller; the device type set for that core port is kept. Not active under netplay. A remap file that stores mapped ports still decides them. The frontend harness gains a first-press lane, and its core declares two controller ports so the lane can check when it is told a port's controller. |
||
|
|
79016ab24f |
Core updater: move the replaced core into the backups instead of compressing it
A setting, Compress Automatic Core Backups (core_updater_auto_backup_compress, on by default), chooses how the automatic backup an update takes is stored. Off, the new core is downloaded and extracted into a staging directory of its own beside the cores (.<core>.staging) and the backup task, in a new install mode (task_push_core_backup_install()), moves the replaced core into the backups as it is - the rzip reader takes an uncompressed .lcbk, and the CRC is in the name - moves the new core into place, and prunes the history: two renames, with the installed core present throughout the download and the extraction. A replaced core that cannot be moved is backed up as a compressed copy; a new core that cannot be moved into place leaves the installed one as it was; a core reached through a symbolic link is updated as before. The staging directory is removed when its download is done, through path_rmdir(), which libretro-common gains with retro_vfs_rmdir_impl() beside mkdir. samples/tasks/core_updater/staged_install_test runs the move, history, fallback, compress, failed and threaded lanes against the shipping updater, decompress, archive, rzip and core backup code with real zips, and runs in Linux-samples-tasks.yml under ASan, UBSan and TSan; vfs_v5_metadata_test gains rmdir checks. |
||
|
|
4592b490bd |
Android: Samsung S-Pen stylus support (#19076)
* android: Add Samsung S-Pen stylus input support Stylus input for the Android driver, squashed from PR #19076 and rebased onto current master (three conflicts, all additive: the hover-guard block in android_input.c, an include in runloop.c and the stylus rows in tools/settings_table.reference). - toolType-based stylus/finger classification with an input-source fallback - hover, contact and click handled separately; hover can move the pointer without pressing - phantom-touch suppression for Samsung pens (hover guard, proximity window) - both barrel buttons read - four Android-only settings: input_stylus_enable, input_stylus_require_contact_for_click, input_stylus_hover_moves_pointer, input_stylus_pressure_sensitivity - android_mouse_calculate_deltas computed its fallback delta from the zeroed delta instead of the event position; fixed Closes #15490 Closes #18194 * overlay: hide for a stylus without unloading the pack The S-Pen support unloaded the on-screen overlay from the run loop whenever a stylus became active and loaded it again two seconds after the last pen event. A pen that leaves and returns does that over and over, and each round is the pack's file and texture work, triggered by input while a core runs. It was a full unload because a soft-hidden overlay still took over pointer input: with input_overlay_pointer_enable the overlay poll tracks every touch as its own pointing device and sets INP_FLAG_BLOCK_POINTER_INPUT, so input_state never reaches the driver and the pen's native state (hover included) is lost. Add INPUT_OVERLAY_STYLUS_HIDDEN and make it complete. While it is set the overlay: - is not drawn (alpha 0, as the gamepad soft-hide does); - reads no touches, so the poll releases whatever a finger was holding the way lifting it would, and no hitbox or pointing device sees the pen; - does not answer mouse, lightgun or pointer queries and does not block pointer input. The run loop sets and clears the flag each frame on the current overlay, the way the gamepad soft-hide is driven. Nothing is unloaded or reloaded. * android: report a pen as one pointer, its barrel button as the right mouse button The S-Pen support gave pointer indices a meaning of its own: index 0 was the cursor, index 1 the tip pressing and index 2 the barrel button, with the pointer count raised to 2 or 3 to match. Only a core written for that contract can read it. Everywhere else an index is a separate touch, so a pressing pen looked like two fingers and a press with the barrel button like three - to cores that count pointers, to the on-screen overlay, and to this driver's own lightgun mapping, where two pointers are turbo and three are reload. With the barrel button held but the tip not pressing, index 1 was stale and still inside the count. Map the pen onto the devices the driver already reports instead: - position is pointer 0, and moves without a press while hovering (input_stylus_hover_moves_pointer); - a press is pointer 0 pressed with a count of 1: the tip pressing past the pressure threshold, or, with input_stylus_require_contact_for_click off, the barrel button, touching or hovering; - the barrel button is the right mouse button. This also puts two settings to work. The contact-for-click option and the pressure threshold only fed a latch nothing read; they now decide the press, as their menu text says. The pen no longer switches the driver into mouse mode (mouse_activated). That flag is never cleared, and once set the lightgun position is read from the mouse coordinates, which a pen does not update, so lightgun aim went stale after the first pen contact. The mouse_x_viewport/mouse_y_viewport values it wrote alongside are not read anywhere. A cancelled pen gesture now releases the press like a lift does; before it fell through to the touchscreen path and left the pen's contact latched. Compiled with the NDK through the Android unity build. Not run on a device: it needs a pass on S-Pen hardware before merging. * android: let a mouse's hover events reach the mouse path again The S-Pen support drops every hover event before the source is looked at, so hover cannot be taken for a touch. A mouse moved with no button held reports its motion as hover events too, and they were dropped with the rest: the pointer only moved while a button was down. Drop hover events after the mouse branch instead. Mouse motion is handled as before the stylus work, and hover from any other source is still kept out of the touchscreen path. --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
74935dc166 |
menu: Menu Frame Rate replaces Throttle Menu Framerate
Menu Frame Rate sets the rate the menu runs at while content is loaded, in every sync mode: 'Display Rate', the default, runs it at the refresh rate of the monitor the window is on, and 'Content Rate' at the content's frame rate. At 'Display Rate' the silence the menu feeds is written without blocking and measured out by the clock, so audio no longer holds the menu to the content's rate; a core left running behind the menu keeps its own clock, running when its next frame is due while the menu re-presents its last frame in between; and behind the threaded video wrapper without the display pacing hold, the timer holds the menu to the display's period. A configuration with Sync to Exact Content Framerate and Throttle Menu Framerate on keeps the content rate it had, and the old key is dropped on save. Display servers gain get_window_refresh_rate, the refresh rate of the output the window is on, for Win32, X11 and SDL, and Wayland reports it from surface enter and leave; the frontend keeps the reading until the window moves, its output changes mode or the menu opens. The pace decision, the menu pacing table, the threaded video harness and the X11 live sample cover both rates. |
||
|
|
6e4d60d553 |
settings: remove the menu_show_help setting
'Show Help' (menu_show_help) gated a main menu Help entry that no longer exists; nothing read the setting, and the previous commits took it out of the Views list and removed the Help list wiring. Drop the def row, its default, the settings_t field and the label string, and regenerate tools/settings_table.reference (the key is the only change). An old retroarch.cfg line for it is ignored. The generated intl tables still name the toggle's VALUE and SUBLABEL ids, so those two stay in msg_hash.h as retired rows until the next Crowdin fetch regenerates the tables without them; a follow-up deletes the two rows then. help_list stays: its "Help" string is the title of the per-setting help popup. |
||
|
|
a2fbc90900 |
Android: enable threaded video by default
DEFAULT_VIDEO_THREADED is now true for ANDROID as well as the Switch, so a fresh Android install runs the core on one thread and presents on another without the user having to turn it on. Only the default changes. A config that already has video_threaded saved keeps its value, and the option is still there under Settings -> Video -> Output to turn it off. tools/settings_table.reference regenerated; only the android profile's video_threaded row changes. |
||
|
|
ba0f7d1c04 |
Android: enable 'Use System Keyboard' by default
DEFAULT_INPUT_ANDROID_SYSTEM_KEYBOARD goes from false to true, so menu text entry on Android opens the system (IME) keyboard out of the box instead of the built-in on-screen keyboard. That gives clipboard paste and password managers without the user having to find the option first. Only the default changes. A config that already has input_android_system_keyboard saved keeps its value, and devices whose input method cannot be driven from a gamepad can still turn the option off under Settings -> Input to get the navigable on-screen keyboard back. The key is registered on every platform but only read under ANDROID, so the reference row changes in all three profiles. The SDL3 option of the same name (input_sdl3_system_keyboard) is left as it was. tools/settings_table.reference regenerated. |
||
|
|
8fbcec8fe4 |
settings_table.reference: add the smb_client_readahead row
The smb_client_readahead setting went in without the reference being regenerated, so the "Settings table drift and griffin-order unity compile" job fails on master: +uint smb_client_readahead 1 0 in all three profiles. Regenerated with python3 tools/settings_check.py table --write tools/settings_table.reference No code change. |
||
|
|
15eddd52b9 |
Video: add Video Filter Threads setting
CPU video filters always ran on one worker per detected core (RARCH_SOFTFILTER_THREADS_AUTO). That suits the heavy filters (NTSC, NTSC CRT, 2xBR) but over-parallelises the light ones: at 256x224 EPX is ~26% slower on eight workers than on one, and LQ2x/2xSaI lose their four-worker gain at eight. video_filter_threads (Settings > Video > Output, under Video Filter; advanced) picks the count: 0 is Automatic, the previous behaviour and the default; 1-16 forces that many workers. Changing it while a filter is running rebuilds the filter on the new count at once; with no filter loaded the value applies at the next load. video_driver_init_filter() now frees a live filter before building the new one. Re-init while content was running (the new setting, and also a video filter path change) previously overwrote state_filter and state_buffer, leaking the old filter, its worker pool and its output buffer. tools/settings_table.reference is regenerated; it had drifted on master (HDR, custom viewport and window size rows) independently of this change. |
||
|
|
1243747c4b |
Config: Convert Ozone 'Color Theme' setting to array (#19451)
* Config: Convert Ozone 'Color Theme' setting to array * Ozone: keep the color theme list in one place The array conversion left the list of themes spelled out three times - the identifier-to-theme table in ozone_set_color_theme(), the identifier-to-label table in menu_setting.c, and the hand-written '|'-separated values string in settings_build_menu() - all of which had to be kept in step by hand for every theme added. Move the list into menu/drivers/ozone_color_themes.h as an X-macro and expand it in both places; the values string is now built from the same list. The frozen legacy numeric table in configuration.c stays separate on purpose. While here: - Left/right on a value that is not in the list (an out-of-range number from an old config, a hand edit) did nothing, leaving only the dropdown to get out. Step from the default theme instead, which is what such a value renders as; the label shown for it is now derived from DEFAULT_OZONE_COLOR_THEME rather than hard-coded. - Find the color theme row to turn into ST_STRING_OPTIONS by its enum rather than by assuming it is the first row of settings_def_ozone_sidebar.h. * Ozone: drop stray struct member, guard a NULL color theme - Remove the unused 'unsigned draw_entry_delay' that the array conversion's conflict resolution left in struct ozone_handle. - ozone_set_color_theme() now strlcpy()s the identifier into last_color_theme, so a NULL would crash. The frame path only runs while the menu is alive, when video_info carries the pointer, but fall back to the default identifier rather than depend on that. --------- Co-authored-by: LibretroAdmin <[email protected]> |
||
|
|
f26a0f09e8 |
menu: limit file extension display to content browsing
filebrowser_parse() backs every file picker, not just Load Content, so Duplicates Only / Never also stripped extensions in the shader, overlay, config, font and playlist pickers. There the extension is often the only thing telling files apart (crt.slangp vs crt.glslp), so apply the setting only to content lists (DISPLAYLIST_DEFAULT and DISPLAYLIST_CORES_DETECTED, minus the overlay, image, video font and playlist-collection pickers that also come through there). Say so in the sublabel. Also move the Always / Duplicates Only / Never strings in msg_hash_us.h from after the multimedia group to just ahead of it, beside the file browser group, and regenerate tools/settings_table.reference for the new setting. |
||
|
|
050200bb62 |
settings: regenerate settings_table.reference
tools/settings_check.py table --check fails on master: two settings were added without the reference being regenerated alongside, so the "Settings table drift" CI job reports drift for every change that follows, whatever it touches. bool audio_fastforward_callback uint menu_thumbnail_preview_threads Regenerated with python3 tools/settings_check.py table --write tools/settings_table.reference Those two rows, in each of the three configurations, are the whole difference. table --check, unity (desktop and mobile) and settings_migrate_selftest.py all pass. |
||
|
|
6c49147fe0 |
input: Background Controller Input setting
Controllers kept driving RetroArch after another window took focus (issue #5004): "Pause Content When Not Active" stops the core, but the menu still navigated and the controller hotkeys (menu toggle, quit, close content, reset, ...) still fired, and with that pause off the core took the input as well. The keyboard is already ignored while unfocused; controllers had no equivalent. New setting input_joypad_background, "Background Controller Input" (Settings > Input), on by default so nothing changes for anyone who drives several instances from one controller. With it off and the window unfocused, every controller read sees an idle pad: the core, the hotkey/menu input gathered by input_driver_collect_system_input(), menu_event() and input_key_pressed(). The gate is one flag, INP_FLAG_JOYPAD_UNFOCUSED, published once per poll, and one accessor, input_driver_joypad_for_read(), which hands the read paths an idle stand-in joypad driver while the flag is set. The real drivers are still polled every frame, so nothing held before focus was lost is replayed afterwards. Rumble, sensors, device names, binding and driver lifetime keep using the real drivers. Joypad and input drivers that have their own notion of background input can follow the same flag and setting. tools/settings_table.reference is regenerated; it also picks up three rows master already emitted but had not recorded (audio_resampler_hq_oversampling, input_sdl3_system_keyboard, audio_latency_floor). |
||
|
|
0ff51eaca7 |
shaders: hotkeys cycle the shader root with remember off
With Remember Last Used Shader Directory off and presets in the root of the Video Shaders directory, Next/Previous Shader cycle those presets, starting from the loaded preset when it is one of them. With the setting on, or when the root holds only RetroArch's own retroarch.* preset, they step from the loaded preset into neighbouring folders, as before. A retroarch.* or auto preset stands in for the preset it references, so a core shader override in the config directory steps from the preset that defines the shader rather than from itself. Cycling the root is the exception: a retroarch.* preset there is one of the presets cycled, so the reference is followed a single hop. A preset loaded as itself anchors where it sits, whatever it references, as Save Preset As writes a simple preset too and following those would cycle a preset's source folder instead of the folder it was saved into. Once a hotkey has applied a preset from the list, the press after it steps from that list rather than anchoring on the preset again: an entry that is itself a simple preset would otherwise resolve back to the entry it references, and the cycling would never leave the pair. The list is dropped when the setting changes, so turning it on follows a retroarch.* preset out of the root that was cycled while it was off. The setting defaults to on. It also opens the shader file browser at the last used folder. samples/gfx/shader_dir_cycle covers which folder the hotkeys cycle for a core override that references a preset in the Video Shaders root, and for the setting changing mid-session. |
||
|
|
b18b044c64 |
menu: add Hide Statistics In Menu, on by default
A new notification-visibility bool, statistics_hide_in_menu, sits under Display Statistics and is shown only while that is on. With it off the menu frame draws the statistics overlay on top of the menu through the OSD font, in every driver, so the MENU UI counters can be read where they are produced. Drivers keep drawing the overlay themselves only when no menu is up, so the default is unchanged. |
||
|
|
9914f465dd |
audio: expose optional pitch-preserving speed playback
Register default-off transport and low-pass settings with audio reinitialization. Prepare the automatic native transport before playback, preserving the ordinary pipeline on unsupported configurations or allocation failure. Keep cold startup out of line and preserve existing boolean field offsets. Cover configured startup and failure in both native lanes and enter the real-wrapper automatic tests through the same settings helper. |
||
|
|
f31daa899a |
settings: regenerate settings_table.reference and the label strings
tools/settings_check.py table --check has been failing on master since audio_block_frames was removed and audio_headphone_virtual_surround, audio_output_layout and save_compression_codec were added without the reference being regenerated; tools/gen_lbl_str.py likewise had three MENU_ENUM_LABEL_*_STR macros to emit for those rows. Both regenerated, nothing else touched: the reference reflects the settings definitions as they are, and the three label strings now exist for the comparison chains that want them. |
||
|
|
851d640841 |
video: threaded video is off by default again
The default went on with the patch that took the last two per-API switches away. The instruction was to make threaded video the default for hardware-rendered cores - which those switches had gated, and which now follow Threaded Video on every API with a ring - not to turn Threaded Video itself on for everyone. This puts the default back where it was: off, with the Switch keeping its own. Nothing else changes; a config that sets video_threaded explicitly is unaffected either way. |