diff --git a/Externals/libretro-common/include/libretro.h b/Externals/libretro-common/include/libretro.h
index b09129494c..e491ad19ec 100644
--- a/Externals/libretro-common/include/libretro.h
+++ b/Externals/libretro-common/include/libretro.h
@@ -722,9 +722,10 @@ enum retro_mod
*/
/**
- * This bit indicates that the associated environment call is experimental,
- * and may be changed or removed in the future.
- * Frontends should mask out this bit before handling the environment call.
+ * This bit used to indicate that the associated environment call is experimental,
+ * and may be changed or removed in the future, but this has since changed.
+ * Frontends should NOT mask out this bit before handling the environment call,
+ * as some callbacks need it to distinguish themselves from others.
*/
#define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000
@@ -1683,6 +1684,22 @@ enum retro_mod
*/
#define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+/**
+ * Notifies the frontend of any quirks associated with serialization.
+ *
+ * Should be set in either \c retro_init or \c retro_load_game, but not both.
+ * @param[in, out] data uint64_t *.
+ * Pointer to the core's serialization quirks.
+ * The frontend will set the flags of the quirks it supports
+ * and clear the flags of those it doesn't.
+ * Behavior is undefined if \c NULL.
+ * @return \c true if this environment call is supported.
+ * @see retro_serialize
+ * @see retro_unserialize
+ * @see RETRO_SERIALIZATION_QUIRK
+ */
+#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 44
+
/**
* The frontend will try to use a "shared" context when setting up a hardware context.
* Mostly applicable to OpenGL.
@@ -1714,6 +1731,24 @@ enum retro_mod
*/
#define RETRO_ENVIRONMENT_GET_VFS_INTERFACE (45 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+/**
+ * Returns a list of frontend-authorized filesystem locations.
+ *
+ * Paths returned by this call must be directly usable with the VFS interface,
+ * for example saf://... on Android.
+ *
+ * @param[out] data struct retro_vfs_authorized_locations *.
+ * The frontend owns the returned pointers. The core must copy strings
+ * if it needs to retain them.
+ * If \c data is \c NULL, the frontend should only return whether this
+ * environment callback is available.
+ *
+ * @return \c true if this environment call is available,
+ * \c false otherwise.
+ * @see RETRO_ENVIRONMENT_GET_VFS_INTERFACE
+ */
+#define RETRO_ENVIRONMENT_GET_VFS_AUTHORIZED_LOCATIONS (93 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
/**
* Returns an interface that the core can use
* to set the state of any accessible device LEDs.
@@ -2700,20 +2735,12 @@ enum retro_mod
#define RETRO_ENVIRONMENT_GET_MEMORY_STATUS (86 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/**
- * Notifies the frontend of any quirks associated with serialization.
+ * Legacy compatibility shim to RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS.
+ * Cores should avoid using this, as it is not as well-supported.
*
- * Should be set in either \c retro_init or \c retro_load_game, but not both.
- * @param[in, out] data uint64_t *.
- * Pointer to the core's serialization quirks.
- * The frontend will set the flags of the quirks it supports
- * and clear the flags of those it doesn't.
- * Behavior is undefined if \c NULL.
- * @return \c true if this environment call is supported.
- * @see retro_serialize
- * @see retro_unserialize
* @see RETRO_SERIALIZATION_QUIRK
*/
-#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 87
+#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS_2 87
/**
* Queries whether the active video driver can present a 10-bit-per-channel
@@ -2853,6 +2880,582 @@ enum retro_mod
*/
#define RETRO_ENVIRONMENT_GET_HDR_MAX_NITS (92 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+/**
+ * Negotiates multi-channel audio output.
+ *
+ * The classic batch callbacks carry interleaved stereo. A core whose
+ * source has more channels - a console with discrete surround, a
+ * media player, an arcade board with a distinct rear pair - has had
+ * to fold them to two at the libretro boundary. This call hands the
+ * core a pair of batch entry points that take a frame of any of the
+ * layouts below, so the channels reach the frontend as they are;
+ * what happens to them then is the frontend's: sent discretely to a
+ * device that has those speakers, folded to stereo for one that does
+ * not, folded and re-expanded as the user's settings say.
+ *
+ * On success the frontend fills the supplied
+ * \c retro_audio_sample_multi_callback: \c batch_int16 always, and
+ * \c batch_float when it also answers \c true to
+ * \c RETRO_ENVIRONMENT_GET_AUDIO_SAMPLE_BATCH_FLOAT (a core wanting
+ * float should query that first; a NULL \c batch_float means int16
+ * only). Either function takes interleaved frames of \c channels
+ * samples, in the ascending-bit order of \c layout - front left,
+ * front right, front centre, LFE, back left, back right, ...
+ * - which is the order the WAVEFORMATEXTENSIBLE channel mask, ALSA,
+ * SDL and the WAV format use. \c channels must equal the number of
+ * bits set in \c layout. The return value has the meaning of
+ * \c retro_audio_sample_batch_t.
+ *
+ * Contract:
+ * - Negotiate once, during \c retro_load_game(). The layout may
+ * change from call to call (a game switching from stereo to 5.1),
+ * but the core commits to one sample format for the loaded game,
+ * as with the float call, and does not mix these entry points
+ * with the classic ones.
+ * - A layout with a bit the frontend does not know, or more than
+ * eight channels, is refused: the call returns 0 frames. Cores
+ * should use the \c RETRO_AUDIO_LAYOUT_ constants.
+ * - The function pointers are owned by the frontend and remain
+ * valid until \c retro_unload_game().
+ * - Frontends that do not recognise this call return \c false; the
+ * core keeps folding to stereo and using the classic callbacks.
+ *
+ * @param[out] data struct retro_audio_sample_multi_callback *.
+ * @return \c true if multi-channel output is supported, \c false otherwise.
+ * @see retro_audio_sample_multi_callback
+ * @see RETRO_AUDIO_SPEAKER_FRONT_LEFT
+ */
+#define RETRO_ENVIRONMENT_GET_AUDIO_SAMPLE_BATCH_MULTI (94 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+enum retro_vr_reference_space {
+ RETRO_VR_REFERENCE_SPACE_LOCAL = 0, /* seated: origin = head at session start/recenter */
+ RETRO_VR_REFERENCE_SPACE_STAGE = 1, /* standing: origin = floor */
+ RETRO_VR_REFERENCE_SPACE_DUMMY = INT_MAX
+};
+
+/**
+ * Describes how the frame the core passes to \c retro_video_refresh_t
+ * divides into views: the screens of a multi-screen system and the eyes
+ * of a stereo image.
+ *
+ * The core keeps drawing every view into its one frame, packed however it
+ * likes, and this call says where each view is. A frontend that presents
+ * views lays them out, shades and composes them itself. Any other frontend
+ * shows the packed frame as it is, so the core needs no second code path.
+ *
+ * Rules:
+ * - Call from \c retro_load_game() or \c retro_run(). The map applies from
+ * the next \c retro_video_refresh_t call and the frontend copies it.
+ * Cores should send it every \c retro_run(): an unchanged map costs a
+ * comparison, and resending covers state loads and run-ahead.
+ * - Rectangles are pixels in the frame of each refresh call (for a
+ * hardware frame, the \c width and \c height passed to the refresh
+ * call), measured from the top-left of the image as displayed. A GL core
+ * rendering bottom-up still measures \c y from the top. Width and height
+ * are non-zero. Rectangles may overlap. A frame that some rectangle does
+ * not fit inside is shown as the packed frame; the map applies again to
+ * the next frame they all fit.
+ * - \c num_views is 1 to \c RETRO_VIDEO_VIEWS_MAX, or 0 to clear the map,
+ * in which case \c views may be NULL.
+ * Screens number from 0 with no gaps; screen 0 is the primary. Each
+ * screen has one \c RETRO_VIDEO_VIEW_EYE_NONE view, or one
+ * \c RETRO_VIDEO_VIEW_EYE_LEFT and one \c RETRO_VIDEO_VIEW_EYE_RIGHT view
+ * of equal width and height.
+ * - \c aspect_ratio is the view's display aspect ratio. At or below zero
+ * means \c width / \c height. Above zero it must be from 0.01 to 100,
+ * or the map is invalid.
+ * - \c retro_game_geometry keeps describing the whole packed frame.
+ * \c RETRO_ENVIRONMENT_SET_ROTATION rotates each view on its own.
+ * - Touch and light gun coordinates stay in packed-frame terms. A touch
+ * on any copy of a screen is reported inside that screen's
+ * \c RETRO_VIDEO_VIEW_EYE_NONE or \c RETRO_VIDEO_VIEW_EYE_LEFT view, at
+ * the same fraction of its width and height as the touch is across the
+ * screen as displayed. Rotation is not undone: x follows the display's
+ * horizontal axis, as it does for a frame without views, so a core that
+ * sets a rotation turns the point itself.
+ * - The frontend clears the map when content unloads.
+ *
+ * @param[in] data const struct retro_video_views *.
+ * @return \c true if the map was accepted. \c false for an invalid map,
+ * which leaves the previous map in effect, or from a frontend that does
+ * not know this call. Either way the core keeps sending the packed frame.
+ * @see retro_video_views
+ * @see RETRO_ENVIRONMENT_GET_VIDEO_VIEWS_STATUS
+ */
+#define RETRO_ENVIRONMENT_SET_VIDEO_VIEWS (95 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/**
+ * Asks whether the frontend presents views, and whether it shows both eyes.
+ *
+ * The answer can change at any time (the user's stereo mode, HDR output,
+ * the video driver), so a core asks every \c retro_run(), before it draws.
+ * - With \c RETRO_VIDEO_VIEWS_STATUS_PRESENTS, the core may pack its frame
+ * for views and send the map with \c RETRO_ENVIRONMENT_SET_VIDEO_VIEWS
+ * before the \c retro_video_refresh_t call of the frame it describes.
+ * - Without it, a core with layouts of its own uses them, and clears any
+ * map it sent by sending one with \c num_views 0.
+ * - With \c RETRO_VIDEO_VIEWS_STATUS_STEREO, a core with stereo content
+ * sends left/right pairs. A screen with no 3D content may still be one
+ * \c RETRO_VIDEO_VIEW_EYE_NONE view, which appears the same in both eyes.
+ * - Without it, the core sends one \c RETRO_VIDEO_VIEW_EYE_NONE view per
+ * screen and can skip rendering the second eye. A left/right pair sent
+ * anyway is shown as its left eye where views are presented, and within
+ * the packed frame elsewhere.
+ *
+ * @param[out] data unsigned *. Set to a combination of the
+ * \c RETRO_VIDEO_VIEWS_STATUS_ flags.
+ * @return \c true if the call is recognised. A frontend that does not know
+ * it returns \c false, which means no flags.
+ * @see RETRO_ENVIRONMENT_SET_VIDEO_VIEWS
+ */
+#define RETRO_ENVIRONMENT_GET_VIDEO_VIEWS_STATUS (96 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/* Flags of RETRO_ENVIRONMENT_GET_VIDEO_VIEWS_STATUS. PRESENTS: the frontend
+ * would lay out a valid map sent now, whether or not one is set. STEREO:
+ * both eyes are shown; only set together with PRESENTS. */
+#define RETRO_VIDEO_VIEWS_STATUS_PRESENTS (1 << 0)
+#define RETRO_VIDEO_VIEWS_STATUS_STEREO (1 << 1)
+/** A headset session is running and head tracking is available:
+ * RETRO_ENVIRONMENT_GET_VR_FRAME_STATE and GET_VR_HEAD_POSE answer, and a
+ * map with RETRO_VIDEO_VIEWS_FLAG_TRACKED becomes the head-tracked view.
+ * Only set together with PRESENTS and STEREO. A core learns about a
+ * headset from this bit only: SET_VIDEO_VIEWS returning true means the
+ * map was accepted, headset or not. */
+#define RETRO_VIDEO_VIEWS_STATUS_HMD (1 << 2)
+
+/* The eye a view shows; see RETRO_ENVIRONMENT_SET_VIDEO_VIEWS. */
+#define RETRO_VIDEO_VIEW_EYE_NONE 0
+#define RETRO_VIDEO_VIEW_EYE_LEFT 1
+#define RETRO_VIDEO_VIEW_EYE_RIGHT 2
+
+/* The most views one map may hold. */
+#define RETRO_VIDEO_VIEWS_MAX 8
+/** The map's one LEFT and one RIGHT view were drawn from this frame's
+ * RETRO_ENVIRONMENT_GET_VR_FRAME_STATE eye poses and fields of view.
+ * While RETRO_VIDEO_VIEWS_STATUS_HMD is set, the frontend shows those two
+ * as the headset's head-tracked view and every other view as a screen.
+ * Without HMD the flag changes nothing. While HMD is set, a map with the
+ * flag and any views must hold exactly one LEFT and one RIGHT view, or it
+ * is refused; an empty map clears the views whatever its flags. */
+#define RETRO_VIDEO_VIEWS_FLAG_TRACKED (1 << 0)
+
+/**
+ * One view of the frame.
+ * @see RETRO_ENVIRONMENT_SET_VIDEO_VIEWS
+ */
+struct retro_video_view
+{
+ /** The view's rectangle in the frame, from the top-left as displayed. */
+ unsigned x;
+ unsigned y;
+ unsigned width;
+ unsigned height;
+ /** Screen number, from 0. */
+ unsigned screen;
+ /** One of the \c RETRO_VIDEO_VIEW_EYE_ values. */
+ unsigned eye;
+ /** Display aspect ratio; at or below zero means \c width / \c height. */
+ float aspect_ratio;
+};
+
+/**
+ * The argument of \c RETRO_ENVIRONMENT_SET_VIDEO_VIEWS.
+ */
+struct retro_video_views
+{
+ const struct retro_video_view *views;
+ unsigned num_views;
+
+ /** RETRO_VIDEO_VIEWS_FLAG_ values. */
+ unsigned flags;
+
+ /**
+ * The space a TRACKED map's poses are in: LOCAL (seated) or STAGE
+ * (standing, LOCAL where the runtime has none).
+ */
+ enum retro_vr_reference_space reference_space;
+
+ /**
+ * Output, while RETRO_VIDEO_VIEWS_STATUS_HMD is set: the recommended
+ * size of one eye's render target; 0 otherwise.
+ * The frontend does not change the core's geometry for it: a core
+ * that draws at this size reports its new frame size with
+ * RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO.
+ */
+ unsigned recommended_view_width;
+ unsigned recommended_view_height;
+};
+
+/* RETRO_VR_FRAME_RECENTERED: the user or the runtime recentered, the
+ * runtime moved STAGE, or a new headset session started, as after a
+ * video driver restart. LOCAL poses then have a new origin; STAGE poses
+ * keep the room's through a recenter. A core that was paused hears of it
+ * at its next frame. A core keeping a reference pose takes it again. */
+#define RETRO_VR_FRAME_RECENTERED (1u << 0)
+#define RETRO_VR_FRAME_TARGET_RESIZED (1u << 1) /* eye size changed: call SET_VIDEO_VIEWS again */
+
+/**
+ * Retrieves the per-eye state plus per-frame event flags
+ * (RETRO_VR_FRAME_RECENTERED / RETRO_VR_FRAME_TARGET_RESIZED) for the
+ * current retro_run() call.
+ *
+ * The frontend samples both eye states once per frame, immediately before
+ * retro_run(), and returns that same sample for the duration of that
+ * retro_run() call.
+ *
+ * The position and orientation in each eye state are the eye-to-tracking
+ * transforms used for that frame. The FOV values are the same values the
+ * frontend must use when submitting the compositor layer for the
+ * corresponding frame.
+ *
+ * @param[out] data struct retro_vr_frame_state *.
+ * @return false if no VR session exists or no valid sample is available.
+ */
+#define RETRO_ENVIRONMENT_GET_VR_FRAME_STATE (97 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+
+/**
+ * Identifies which eye a \ref retro_vr_eye_state describes, and the index
+ * of that eye within the two-element array passed to
+ * RETRO_ENVIRONMENT_GET_VR_FRAME_STATE.
+ */
+enum retro_vr_eye
+{
+ RETRO_VR_EYE_LEFT = 0,
+ RETRO_VR_EYE_RIGHT = 1,
+
+ /** @private Defined to ensure sizeof(retro_vr_eye) == sizeof(int). Do not use. */
+ RETRO_VR_EYE_DUMMY = INT_MAX
+};
+
+/**
+ * One eye's head-tracked pose and asymmetric field of view for the current
+ * frame, in the frontend's tracking space (meters, right-handed, Y up).
+ *
+ * position and orientation describe the eye-to-tracking-space transform,
+ * not its inverse.
+ *
+ * The quaternion is stored in (x, y, z, w) order. This is intentionally
+ * different from APIs such as GLM where glm::quat is constructed as
+ * (w, x, y, z).
+ *
+ * @see RETRO_ENVIRONMENT_GET_VR_FRAME_STATE
+ */
+struct retro_vr_eye_state
+{
+ /**
+ * Eye position in tracking space, in meters.
+ */
+ float position[3];
+
+ /**
+ * Eye orientation in tracking space, as a quaternion (x, y, z, w).
+ *
+ * This is the orientation of the eye relative to tracking space, not
+ * the inverse/view orientation.
+ */
+ float orientation[4];
+
+ /**
+ * Tangent of the positive half-angle from the eye's forward axis to
+ * each frustum edge, in the order:
+ *
+ * [0] left
+ * [1] right
+ * [2] up
+ * [3] down
+ *
+ * All four values are positive magnitudes. They describe the distance
+ * of each frustum edge from the forward axis in tangent space; they are
+ * not signed OpenXR angles.
+ *
+ * The four values are independent because HMD eye frustums are normally
+ * asymmetric.
+ *
+ * For example, if the left and right tangent values are l and r, and
+ * the up and down tangent values are u and d, the projection frustum
+ * spans:
+ *
+ * left = -l
+ * right = r
+ * bottom = -d
+ * top = u
+ *
+ * A frontend converting from APIs that represent the corresponding
+ * angles as signed values must use their magnitudes.
+ */
+ float fov_tan[4];
+};
+
+struct retro_vr_frame_state {
+ struct retro_vr_eye_state eyes[2];
+ uint32_t flags;
+};
+
+/**
+ * Retrieves the current head-tracked pose.
+ *
+ * The frontend samples the HMD once per frame, immediately before
+ * retro_run(), and returns the same sample for the duration of that
+ * retro_run() call.
+ */
+#define RETRO_ENVIRONMENT_GET_VR_HEAD_POSE (98 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/* Frontend H.264 decoding to CPU-readable 8-bit YUV 4:2:0 planes.
+ * Version must be initialized to 1 before querying. Calls for each decoder
+ * are serialized by the core; close all decoders before context teardown.
+ * Unsupported frontends return false. Input is one Annex-B access unit.
+ * Planes remain valid until decode, drain or close. */
+#define RETRO_ENVIRONMENT_GET_VIDEO_DECODE_INTERFACE (99 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+#define RETRO_VIDEO_DECODE_INTERFACE_VERSION 1
+
+struct retro_video_decode_frame
+{
+ const uint8_t *planes[3];
+ unsigned strides[3];
+ unsigned width, height;
+ bool hardware;
+};
+
+struct retro_video_decode_interface
+{
+ unsigned interface_version;
+ void *(*open)(void);
+ /* 1: frame ready; 0: consumed, delayed output; -1: failure. */
+ int (*decode)(void *decoder, const uint8_t *data, size_t size,
+ struct retro_video_decode_frame *frame);
+ /* 1: frame ready; 0: no more frames; -1: failure. */
+ int (*drain)(void *decoder, struct retro_video_decode_frame *frame);
+ void (*close)(void *decoder);
+};
+
+
+
+#define RETRO_VR_HEAD_POSE_POSITION_VALID (1u << 0)
+#define RETRO_VR_HEAD_POSE_ORIENTATION_VALID (1u << 1)
+#define RETRO_VR_HEAD_POSE_VELOCITY_VALID (1u << 2)
+
+/**
+ * Head pose and motion in the frontend's tracking space (meters,
+ * seconds, right-handed, Y up).
+ *
+ * position and orientation describe the head-to-tracking-space transform,
+ * not its inverse. The orientation quaternion rotates vectors from head
+ * space into tracking space.
+ *
+ * linear_velocity is the velocity of the head-space origin, expressed in
+ * tracking-space coordinates. angular_velocity is the head's angular
+ * velocity, expressed in tracking-space coordinates. Velocity fields are
+ * valid only when RETRO_VR_HEAD_POSE_VELOCITY_VALID is set.
+ */
+struct retro_vr_head_pose
+{
+ /** Head position in tracking space, in meters. */
+ float position[3];
+
+ /** Head orientation in tracking space, quaternion (x, y, z, w). */
+ float orientation[4];
+
+ /** Linear velocity in meters/second. */
+ float linear_velocity[3];
+
+ /** Angular velocity in radians/second. */
+ float angular_velocity[3];
+
+ /* orientation/position are valid */
+ uint32_t flags;
+};
+
+/**
+ * Retrieves where the user's hands are: each motion controller's poses
+ * and each hand's joints.
+ *
+ * A core reads a pose, a joint or a field only when its flag says so.
+ *
+ * The three poses are:
+ * - grip: /input/grip/pose, at the palm's
+ * centre when the hand closes on the controller. -Z runs through the
+ * tube the closed fingers make, from the little finger toward the
+ * thumb; +X is normal to the flat palm, away from it on the left
+ * hand and into it on the right.
+ * - aim: /input/aim/pose, the pointing ray, -Z forward from the
+ * controller's tip: where a laser or a shot starts.
+ * - palm: the palm's centre at its surface, -Z the way
+ * a straightened index finger points, +X as for grip: where a hand
+ * model goes.
+ *
+ * A runtime that drives a controller profile from a bare tracked hand
+ * reports that hand as active, with grip and aim taken from the hand.
+ * The poses and joints are those the frontend sampled for this run,
+ * before retro_run(); call it from retro_run().
+ *
+ * @param[out] data struct retro_vr_controller_poses *.
+ * @return false if no VR session exists or no valid sample is available,
+ * and from a frontend that does not know the call. true with neither
+ * hand RETRO_VR_HAND_ACTIVE: a session that sees no hands.
+ * @see retro_vr_controller_poses
+ */
+#define RETRO_ENVIRONMENT_GET_VR_CONTROLLER_POSES (100 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/** Which hand a \ref retro_vr_hand_state describes, and its index in
+ * \ref retro_vr_controller_poses::hands. */
+enum retro_vr_hand
+{
+ RETRO_VR_HAND_LEFT = 0,
+ RETRO_VR_HAND_RIGHT = 1,
+ RETRO_VR_HAND_COUNT = 2,
+
+ /** @private Defined to ensure sizeof(retro_vr_hand) == sizeof(int). Do not use. */
+ RETRO_VR_HAND_DUMMY = INT_MAX
+};
+
+/* Flags of retro_vr_pose: OpenXR's XrSpaceLocationFlags and
+ * XrSpaceVelocityFlags. VALID: the value may be used. TRACKED: it is
+ * measured now rather than estimated, as a controller out of the
+ * headset's cameras' view keeps an estimated position for a while. */
+#define RETRO_VR_POSE_ORIENTATION_VALID (1u << 0)
+#define RETRO_VR_POSE_POSITION_VALID (1u << 1)
+#define RETRO_VR_POSE_ORIENTATION_TRACKED (1u << 2)
+#define RETRO_VR_POSE_POSITION_TRACKED (1u << 3)
+#define RETRO_VR_POSE_LINEAR_VELOCITY_VALID (1u << 4)
+#define RETRO_VR_POSE_ANGULAR_VELOCITY_VALID (1u << 5)
+
+/**
+ * A pose and its motion in the tracking space.
+ *
+ * position and orientation are the pose-to-tracking-space transform.
+ * linear_velocity is the origin's velocity in meters per second;
+ * angular_velocity the rotation axis scaled by radians per second.
+ * Both are in tracking-space coordinates.
+ */
+struct retro_vr_pose
+{
+ float position[3];
+ float orientation[4];
+ float linear_velocity[3];
+ float angular_velocity[3];
+ /** RETRO_VR_POSE_ flags; 0 when nothing is known. */
+ uint32_t flags;
+};
+
+/**
+ * A hand's joints, in XR_EXT_hand_tracking's XrHandJointEXT order.
+ * Each joint's -Z points along its bone toward the fingertip, +Y out of
+ * the back of the hand. The palm joint is at the centre of the middle
+ * finger's metacarpal; the wrist joint at the wrist.
+ *
+ * The set is XR_EXT_hand_tracking's default one and is fixed: a larger
+ * joint set comes through a call of its own.
+ */
+enum retro_vr_hand_joint
+{
+ RETRO_VR_HAND_JOINT_PALM = 0,
+ RETRO_VR_HAND_JOINT_WRIST,
+ RETRO_VR_HAND_JOINT_THUMB_METACARPAL,
+ RETRO_VR_HAND_JOINT_THUMB_PROXIMAL,
+ RETRO_VR_HAND_JOINT_THUMB_DISTAL,
+ RETRO_VR_HAND_JOINT_THUMB_TIP,
+ RETRO_VR_HAND_JOINT_INDEX_METACARPAL,
+ RETRO_VR_HAND_JOINT_INDEX_PROXIMAL,
+ RETRO_VR_HAND_JOINT_INDEX_INTERMEDIATE,
+ RETRO_VR_HAND_JOINT_INDEX_DISTAL,
+ RETRO_VR_HAND_JOINT_INDEX_TIP,
+ RETRO_VR_HAND_JOINT_MIDDLE_METACARPAL,
+ RETRO_VR_HAND_JOINT_MIDDLE_PROXIMAL,
+ RETRO_VR_HAND_JOINT_MIDDLE_INTERMEDIATE,
+ RETRO_VR_HAND_JOINT_MIDDLE_DISTAL,
+ RETRO_VR_HAND_JOINT_MIDDLE_TIP,
+ RETRO_VR_HAND_JOINT_RING_METACARPAL,
+ RETRO_VR_HAND_JOINT_RING_PROXIMAL,
+ RETRO_VR_HAND_JOINT_RING_INTERMEDIATE,
+ RETRO_VR_HAND_JOINT_RING_DISTAL,
+ RETRO_VR_HAND_JOINT_RING_TIP,
+ RETRO_VR_HAND_JOINT_LITTLE_METACARPAL,
+ RETRO_VR_HAND_JOINT_LITTLE_PROXIMAL,
+ RETRO_VR_HAND_JOINT_LITTLE_INTERMEDIATE,
+ RETRO_VR_HAND_JOINT_LITTLE_DISTAL,
+ RETRO_VR_HAND_JOINT_LITTLE_TIP,
+ RETRO_VR_HAND_JOINT_COUNT,
+
+ /** @private Defined to ensure sizeof(retro_vr_hand_joint) == sizeof(int). Do not use. */
+ RETRO_VR_HAND_JOINT_DUMMY = INT_MAX
+};
+
+/** One joint: its pose, and the distance from it to the skin in meters
+ * (0 when unknown). */
+struct retro_vr_hand_joint_state
+{
+ struct retro_vr_pose pose;
+ float radius;
+};
+
+/* Flags of retro_vr_hand_state. */
+/** Something is in the hand: a controller or a tracked hand. Without
+ * it the rest of the hand's state is cleared. */
+#define RETRO_VR_HAND_ACTIVE (1u << 0)
+/** The joints are filled: the frontend tracks this hand's joints now. */
+#define RETRO_VR_HAND_JOINTS (1u << 1)
+/** The joints are seen by the headset, the hand bare. */
+#define RETRO_VR_HAND_JOINTS_OPTICAL (1u << 2)
+/** The joints are estimated from a held controller's grip and touch
+ * sensors. With neither JOINTS_ source bit, the source is unknown. */
+#define RETRO_VR_HAND_JOINTS_CONTROLLER (1u << 3)
+
+/** One hand. */
+struct retro_vr_hand_state
+{
+ /** The OpenXR interaction profile path the hand is used through,
+ * such as "/interaction_profiles/oculus/touch_controller"; NULL when
+ * none is known. The string is the frontend's, valid until the next
+ * RETRO_ENVIRONMENT_GET_VR_CONTROLLER_POSES call or until
+ * retro_run() returns, whichever comes first. */
+ const char *interaction_profile;
+
+ /** RETRO_VR_HAND_ flags. */
+ uint32_t flags;
+
+ struct retro_vr_pose grip;
+ struct retro_vr_pose aim;
+ struct retro_vr_pose palm;
+
+ /** Valid while RETRO_VR_HAND_JOINTS is set, each joint by its own
+ * pose flags. */
+ struct retro_vr_hand_joint_state joints[RETRO_VR_HAND_JOINT_COUNT];
+};
+
+/** The argument of RETRO_ENVIRONMENT_GET_VR_CONTROLLER_POSES. */
+struct retro_vr_controller_poses
+{
+ /** Indexed by enum retro_vr_hand. */
+ struct retro_vr_hand_state hands[RETRO_VR_HAND_COUNT];
+};
+
+/* Speaker positions, as bits of a layout mask; a frame's channels are
+ * interleaved in ascending bit order. The bits are those of the
+ * WAVEFORMATEXTENSIBLE channel mask. */
+#define RETRO_AUDIO_SPEAKER_FRONT_LEFT 0x001
+#define RETRO_AUDIO_SPEAKER_FRONT_RIGHT 0x002
+#define RETRO_AUDIO_SPEAKER_FRONT_CENTER 0x004
+#define RETRO_AUDIO_SPEAKER_LOW_FREQUENCY 0x008
+#define RETRO_AUDIO_SPEAKER_BACK_LEFT 0x010
+#define RETRO_AUDIO_SPEAKER_BACK_RIGHT 0x020
+#define RETRO_AUDIO_SPEAKER_FRONT_LEFT_OF_CENTER 0x040
+#define RETRO_AUDIO_SPEAKER_FRONT_RIGHT_OF_CENTER 0x080
+#define RETRO_AUDIO_SPEAKER_BACK_CENTER 0x100
+#define RETRO_AUDIO_SPEAKER_SIDE_LEFT 0x200
+#define RETRO_AUDIO_SPEAKER_SIDE_RIGHT 0x400
+
+/* The layouts a core is expected to use. Others are accepted where the
+ * frontend knows every bit. */
+#define RETRO_AUDIO_LAYOUT_MONO (RETRO_AUDIO_SPEAKER_FRONT_CENTER)
+#define RETRO_AUDIO_LAYOUT_STEREO (RETRO_AUDIO_SPEAKER_FRONT_LEFT | RETRO_AUDIO_SPEAKER_FRONT_RIGHT)
+#define RETRO_AUDIO_LAYOUT_2_1 (RETRO_AUDIO_LAYOUT_STEREO | RETRO_AUDIO_SPEAKER_LOW_FREQUENCY)
+#define RETRO_AUDIO_LAYOUT_QUAD (RETRO_AUDIO_LAYOUT_STEREO | RETRO_AUDIO_SPEAKER_BACK_LEFT | RETRO_AUDIO_SPEAKER_BACK_RIGHT)
+#define RETRO_AUDIO_LAYOUT_5_1 (RETRO_AUDIO_LAYOUT_QUAD | RETRO_AUDIO_SPEAKER_FRONT_CENTER | RETRO_AUDIO_SPEAKER_LOW_FREQUENCY)
+#define RETRO_AUDIO_LAYOUT_5_1_SIDE (RETRO_AUDIO_LAYOUT_STEREO | RETRO_AUDIO_SPEAKER_FRONT_CENTER | RETRO_AUDIO_SPEAKER_LOW_FREQUENCY \
+ | RETRO_AUDIO_SPEAKER_SIDE_LEFT | RETRO_AUDIO_SPEAKER_SIDE_RIGHT)
+#define RETRO_AUDIO_LAYOUT_7_1 (RETRO_AUDIO_LAYOUT_5_1 | RETRO_AUDIO_SPEAKER_SIDE_LEFT | RETRO_AUDIO_SPEAKER_SIDE_RIGHT)
+
/**
* Result of \c RETRO_ENVIRONMENT_GET_MEMORY_STATUS.
*
@@ -2976,6 +3579,20 @@ struct retro_vfs_dir_handle;
*/
#define RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS (1 << 0)
+/**
+ * Indicates that the file will be read once, from start to finish,
+ * and then closed.
+ *
+ * No mapping or caching is wanted: the caller already keeps the bytes
+ * it asked for, so anything the frontend holds on to beyond the call
+ * is dead weight. A frontend that buffers its reads may wish to skip
+ * doing so for such a stream, since a whole-file read gains nothing
+ * from being split across a buffer and copied twice.
+ *
+ * Only meaningful together with \c RETRO_VFS_FILE_ACCESS_READ.
+ */
+#define RETRO_VFS_FILE_ACCESS_HINT_SEQUENTIAL_BULK (1 << 1)
+
/** @} */
/** @defgroup RETRO_VFS_SEEK_POSITION File Seek Positions
@@ -3020,8 +3637,49 @@ struct retro_vfs_dir_handle;
*/
#define RETRO_VFS_STAT_IS_CHARACTER_SPECIAL (1 << 2)
+/**
+ * Indicates that the current user cannot write to the given path.
+ * POSIX: the owner write bit is clear.
+ * Windows/UWP: \c FILE_ATTRIBUTE_READONLY is set.
+ * Frontends that cannot determine this never set the flag.
+ * @since VFS API v5
+ */
+#define RETRO_VFS_STAT_IS_READONLY (1 << 3)
+
/** @} */
+/**
+ * @defgroup RETRO_VFS_COPY Copy Flags
+ * @since VFS API v5
+ * @{
+ */
+
+/** Replace \c dst if it already exists. Without it an existing \c dst is an error. */
+#define RETRO_VFS_COPY_OVERWRITE (1 << 0)
+
+/** @} */
+
+/**
+ * @defgroup RETRO_VFS_COPY_STATUS Copy Status
+ * Values returned by \c retro_vfs_copy_step_t.
+ * @since VFS API v5
+ * @{
+ */
+/** The copy is still in progress. */
+#define RETRO_VFS_COPY_RUNNING (0)
+/** The copy completed; \c dst is complete and closed. */
+#define RETRO_VFS_COPY_DONE (1)
+/** The copy failed or was cancelled; no partial \c dst remains. */
+#define RETRO_VFS_COPY_FAILED (-1)
+/** @} */
+
+/**
+ * Opaque handle to an in-progress file copy.
+ * @see retro_vfs_copy_begin_t
+ * @since VFS API v5
+ */
+struct retro_vfs_copy_handle;
+
/**
* Returns the path that was used to open this file.
*
@@ -3213,6 +3871,124 @@ typedef int (RETRO_CALLCONV *retro_vfs_stat_t)(const char *path, int32_t *size);
*/
typedef int (RETRO_CALLCONV *retro_vfs_stat_64_t)(const char *path, int64_t *size);
+/**
+ * Sets or clears the read-only state of a file or directory.
+ *
+ * POSIX: sets or clears the write bits of the mode, leaving the rest intact.
+ * Windows/UWP: sets or clears \c FILE_ATTRIBUTE_READONLY.
+ *
+ * @param path The path to the file or directory.
+ * @param readonly Non-zero to make the path read-only,
+ * zero to make it writable.
+ * @return 0 on success,
+ * or -1 if \c path does not exist or the platform or file system
+ * cannot store a read-only state.
+ * @see path_set_readonly
+ * @see RETRO_VFS_STAT_IS_READONLY
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_set_readonly_t)(const char *path, int readonly);
+
+/**
+ * Gets the last modification time of a file or directory.
+ *
+ * @param path The path to the file or directory.
+ * @param[out] mtime Set to the modification time
+ * in seconds since 1970-01-01T00:00:00Z. May be negative.
+ * @return 0 on success,
+ * or -1 if \c path does not exist or the platform
+ * cannot report a modification time.
+ * @see path_get_mtime
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_get_mtime_t)(const char *path, int64_t *mtime);
+
+/**
+ * Sets the last modification time of a file or directory.
+ *
+ * The frontend rounds to the file system's resolution,
+ * so a following \c retro_vfs_get_mtime_t may report a different value.
+ *
+ * @param path The path to the file or directory.
+ * @param mtime The modification time in seconds since 1970-01-01T00:00:00Z.
+ * @return 0 on success,
+ * or -1 if \c path does not exist or the platform or file system
+ * does not allow the modification time to be set.
+ * @see path_set_mtime
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_set_mtime_t)(const char *path, int64_t mtime);
+
+/**
+ * Starts copying a single regular file and returns without moving any of it.
+ *
+ * A copy is a resumable operation that the caller advances with
+ * \c retro_vfs_copy_step_t, each step bounded by a byte budget the caller
+ * chooses. The frontend keeps no thread and holds no lock for it; a caller
+ * that wants the transfer off its own thread drives the steps from wherever
+ * it likes. No call in this group ever waits for more than the requested
+ * step.
+ *
+ * \c dst is the full path of the new file, not a directory; missing parent
+ * directories are created. Metadata (modification time, read-only state)
+ * of \c dst after the copy is platform-defined. Either path may belong to
+ * any file system the frontend supports.
+ *
+ * Checks that can be made up front (missing or non-regular \c src,
+ * \c dst is a directory, \c dst exists without \c RETRO_VFS_COPY_OVERWRITE,
+ * \c src equals \c dst) fail here by returning \c NULL.
+ *
+ * @param src The path to the file to copy. Must be a regular file.
+ * @param dst The full path of the destination file. Must differ from \c src.
+ * @param flags Bitwise combination of \c RETRO_VFS_COPY flags, or 0.
+ * @return A handle to poll and close, or \c NULL if the copy could not start.
+ * @see retro_vfs_copy_step_t
+ * @see retro_vfs_copy_close_t
+ * @see filestream_copy_begin
+ * @see RETRO_VFS_COPY
+ * @since VFS API v5
+ */
+typedef struct retro_vfs_copy_handle *(RETRO_CALLCONV *retro_vfs_copy_begin_t)(const char *src, const char *dst, unsigned flags);
+
+/**
+ * Advances a copy started with \c retro_vfs_copy_begin_t by at most
+ * \c max_bytes and reports its state.
+ *
+ * The budget is the caller's latency/throughput dial: a few MiB from a
+ * frame loop keeps each call short; a very large budget (or repeated calls
+ * until the status leaves \c RETRO_VFS_COPY_RUNNING) runs the transfer at
+ * the full speed of the platform's copy primitive with no user-space
+ * buffer where the kernel can move the bytes itself.
+ *
+ * A step never moves more than \c max_bytes, but it may move less, and it
+ * may report \c RETRO_VFS_COPY_DONE early if the platform completed the
+ * copy without moving bytes (e.g. a file-system clone).
+ *
+ * @param handle The copy.
+ * @param max_bytes Upper bound on bytes moved by this call; 0 selects a
+ * frontend default sized for a frame loop (a few MiB).
+ * @param[out] bytes_done Bytes written to \c dst so far. May be \c NULL.
+ * @param[out] bytes_total Size of \c src in bytes. May be \c NULL.
+ * @return One of the \c RETRO_VFS_COPY_STATUS values.
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_copy_step_t)(struct retro_vfs_copy_handle *handle, int64_t max_bytes, int64_t *bytes_done, int64_t *bytes_total);
+
+/**
+ * Releases a copy handle.
+ *
+ * If the copy is still running it is cancelled and the partial \c dst
+ * removed; nothing is waited for. Must be called exactly once for every
+ * non-NULL handle from \c retro_vfs_copy_begin_t, whatever
+ * \c retro_vfs_copy_step_t reported.
+ *
+ * @param handle The copy.
+ * @return 0 if the copy had completed successfully, or -1 if it failed,
+ * was cancelled, or was still running when closed.
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_copy_close_t)(struct retro_vfs_copy_handle *handle);
+
/**
* Creates a directory at the given path.
*
@@ -3285,6 +4061,29 @@ typedef const char *(RETRO_CALLCONV *retro_vfs_dirent_get_name_t)(struct retro_v
*/
typedef bool (RETRO_CALLCONV *retro_vfs_dirent_is_dir_t)(struct retro_vfs_dir_handle *dirstream);
+/**
+ * Gets information about the directory entry most recently returned by
+ * \c retro_vfs_readdir_t, without opening it or building its path.
+ *
+ * Only valid after a \c retro_vfs_readdir_t call that returned \c true,
+ * and before the next \c retro_vfs_readdir_t or \c retro_vfs_closedir_t
+ * call on the same handle.
+ *
+ * @param dirstream The directory being enumerated.
+ * @param[out] size The entry's size in bytes (0 for directories).
+ * May be \c NULL, in which case this value is ignored.
+ * @param[out] mtime The entry's modification time
+ * in seconds since 1970-01-01T00:00:00Z.
+ * May be \c NULL, in which case this value is ignored.
+ * @return A bitmask of \c RETRO_VFS_STAT flags for the entry
+ * (\c RETRO_VFS_STAT_IS_VALID is always set on success),
+ * or 0 if the frontend cannot provide entry information.
+ * @see retro_dirent_stat
+ * @see RETRO_VFS_STAT
+ * @since VFS API v5
+ */
+typedef int (RETRO_CALLCONV *retro_vfs_dirent_stat_t)(struct retro_vfs_dir_handle *dirstream, int64_t *size, int64_t *mtime);
+
/**
* Closes the given directory and release its resources.
*
@@ -3372,6 +4171,28 @@ struct retro_vfs_interface
/* VFS API v4 */
/** @copydoc retro_vfs_stat_64_t */
retro_vfs_stat_64_t stat_64;
+
+ /* VFS API v5 */
+ /** @copydoc retro_vfs_set_readonly_t */
+ retro_vfs_set_readonly_t set_readonly;
+
+ /** @copydoc retro_vfs_get_mtime_t */
+ retro_vfs_get_mtime_t get_mtime;
+
+ /** @copydoc retro_vfs_set_mtime_t */
+ retro_vfs_set_mtime_t set_mtime;
+
+ /** @copydoc retro_vfs_copy_begin_t */
+ retro_vfs_copy_begin_t copy_begin;
+
+ /** @copydoc retro_vfs_copy_step_t */
+ retro_vfs_copy_step_t copy_step;
+
+ /** @copydoc retro_vfs_copy_close_t */
+ retro_vfs_copy_close_t copy_close;
+
+ /** @copydoc retro_vfs_dirent_stat_t */
+ retro_vfs_dirent_stat_t dirent_stat;
};
/**
@@ -3413,6 +4234,33 @@ struct retro_vfs_interface_info
struct retro_vfs_interface *iface;
};
+/**
+ * Represents a single frontend-authorized filesystem location.
+ *
+ * The \c path field must be directly usable through the frontend VFS
+ * interface, for example saf://... on Android.
+ *
+ * The frontend owns all returned pointers. Cores must copy strings if they
+ * need to retain them after the environment callback returns.
+ */
+struct retro_vfs_authorized_location
+{
+ const char *path;
+ const char *label;
+ unsigned flags;
+};
+
+/**
+ * Represents the list of frontend-authorized filesystem locations.
+ *
+ * This is returned by RETRO_ENVIRONMENT_GET_VFS_AUTHORIZED_LOCATIONS.
+ */
+struct retro_vfs_authorized_locations
+{
+ const struct retro_vfs_authorized_location *locations;
+ size_t count;
+};
+
/** @} */
/** @defgroup GET_HW_RENDER_INTERFACE Hardware Rendering Interface
@@ -3437,7 +4285,10 @@ enum retro_hw_render_interface_type
*/
RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
- /** Indicates a \c retro_hw_render_interface for Direct3D 9. */
+ /**
+ * Indicates a \c retro_hw_render_interface for Direct3D 9.
+ * @see retro_hw_render_interface_d3d9
+ */
RETRO_HW_RENDER_INTERFACE_D3D9 = 1,
/** Indicates a \c retro_hw_render_interface for Direct3D 10. */
@@ -3696,6 +4547,20 @@ enum retro_hw_render_context_negotiation_interface_type
*/
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN = 0,
+ /**
+ * Denotes a context negotiation interface for Direct3D 12.
+ * Carries the highest hardware render interface version the core can use.
+ * @see retro_hw_render_context_negotiation_interface_d3d12
+ */
+ RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D12 = 1,
+
+ /**
+ * Denotes a context negotiation interface for Direct3D 11.
+ * Carries the highest hardware render interface version the core can use.
+ * @see retro_hw_render_context_negotiation_interface_d3d11
+ */
+ RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_D3D11 = 2,
+
/**
* @private Defined to ensure sizeof(retro_hw_render_context_negotiation_interface_type) == sizeof(int).
* Do not use.
@@ -4539,6 +5404,52 @@ struct retro_log_callback
*/
#define RETRO_SIMD_CRC32 (1 << 25)
+/**
+ * Indicates CPU support for hardware SHA-512 acceleration.
+ *
+ * On AArch64 this is FEAT_SHA512, optional from Armv8.1 and A64-only.
+ * On x86 it is the SHA512 instruction group enumerated by
+ * CPUID.(EAX=07H,ECX=1):EAX[0], which is separate from the SHA-NI
+ * instructions covering SHA-1 and SHA-256.
+ */
+#define RETRO_SIMD_SHA512 (1 << 26)
+
+/**
+ * Indicates CPU support for hardware SHA-1 acceleration.
+ *
+ * On AArch64 this is FEAT_SHA1; on x86 it is part of SHA-NI, which
+ * covers SHA-1 and SHA-256 in one CPUID bit and therefore always
+ * reports alongside \c RETRO_SIMD_SHA256 there.
+ */
+#define RETRO_SIMD_SHA1 (1 << 27)
+
+/**
+ * Indicates CPU support for hardware SHA-256 acceleration.
+ *
+ * On AArch64 this is FEAT_SHA256; on x86 it is the other half of
+ * SHA-NI. Separate from \c RETRO_SIMD_SHA1 because AArch64 enumerates
+ * the two independently.
+ */
+#define RETRO_SIMD_SHA256 (1 << 28)
+
+/**
+ * Indicates CPU support for the FMA3 fused multiply-add instructions.
+ *
+ * CPUID.(EAX=01H):ECX[12]. They operate on YMM state, so this reports
+ * only where the operating system preserves it, as \c RETRO_SIMD_AVX
+ * does.
+ */
+#define RETRO_SIMD_FMA3 (1 << 29)
+
+/**
+ * Indicates CPU support for the FMA4 fused multiply-add instructions.
+ *
+ * CPUID.(EAX=80000001H):ECX[16], an AMD extension dropped from Zen, and
+ * a different encoding from \c RETRO_SIMD_FMA3 rather than a superset
+ * of it. Gated on the same operating system state.
+ */
+#define RETRO_SIMD_FMA4 (1 << 30)
+
/** @} */
/**
@@ -4945,6 +5856,19 @@ enum retro_camera_buffer
*/
RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER,
+ /**
+ * Indicates that camera frames should be delivered to the core as
+ * the planar 4:2:0 YCbCr the capture device produces, in place.
+ *
+ * No conversion happens between the device and the core: the planes
+ * are the capture buffer itself. A core that can sample or convert
+ * YCbCr should prefer this over \c RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER,
+ * which costs the frontend a conversion pass per frame.
+ *
+ * @see retro_camera_frame_planar_t
+ */
+ RETRO_CAMERA_BUFFER_PLANAR,
+
/**
* @private Defined to ensure sizeof(enum retro_camera_buffer) == sizeof(int).
* Do not use.
@@ -4952,6 +5876,44 @@ enum retro_camera_buffer
RETRO_CAMERA_BUFFER_DUMMY = INT_MAX
};
+/** The luma plane is full range (0..255) rather than limited (16..235). */
+#define RETRO_CAMERA_PLANAR_FULL_RANGE (1 << 0)
+/** BT.709 matrix rather than BT.601. */
+#define RETRO_CAMERA_PLANAR_BT709 (1 << 1)
+
+/**
+ * One 8-bit 4:2:0 YCbCr frame, as planes.
+ *
+ * Covers NV12, NV21 and I420 without a format tag: the chroma planes
+ * are addressed by their own base pointers and a common sample step,
+ * so NV12 is planes[2] == planes[1] + 1 with \c chroma_step 2,
+ * NV21 the other way round, and I420 two separate planes with
+ * \c chroma_step 1. Row pairs share a chroma row.
+ *
+ * @see RETRO_CAMERA_BUFFER_PLANAR
+ */
+struct retro_camera_planar_frame
+{
+ /** Y, Cb, Cr. Owned by the frontend, read-only, valid for the duration of the callback. */
+ const uint8_t *planes[3];
+
+ /** Row strides of the three planes, in bytes. */
+ size_t strides[3];
+
+ /** Distance between horizontally adjacent chroma samples of one plane, in bytes: 1 or 2. */
+ size_t chroma_step;
+
+ /** Frame size in luma pixels. */
+ unsigned width;
+ unsigned height;
+
+ /** Degrees the frame must be rotated clockwise to be upright: 0, 90, 180 or 270. */
+ unsigned rotation;
+
+ /** \c RETRO_CAMERA_PLANAR_* bits. */
+ unsigned flags;
+};
+
/**
* Starts an initialized camera.
* The camera is disabled by default,
@@ -5030,6 +5992,17 @@ typedef void (RETRO_CALLCONV *retro_camera_frame_raw_framebuffer_t)(const uint32
typedef void (RETRO_CALLCONV *retro_camera_frame_opengl_texture_t)(unsigned texture_id,
unsigned texture_target, const float *affine);
+/**
+ * Called by the frontend to report a new camera frame,
+ * delivered as the capture device's own planar YCbCr buffer.
+ *
+ * @param frame The frame. Its planes may be invalidated when this function returns,
+ * so the core should make its own copy if necessary.
+ * @see RETRO_CAMERA_BUFFER_PLANAR
+ */
+typedef void (RETRO_CALLCONV *retro_camera_frame_planar_t)(
+ const struct retro_camera_planar_frame *frame);
+
/**
* An interface that the core can use to access a device's camera.
*
@@ -5105,6 +6078,15 @@ struct retro_camera_callback
* May be \c NULL, in which case this function is skipped.
*/
retro_camera_lifetime_status_t deinitialized;
+
+ /**
+ * @copydoc retro_camera_frame_planar_t
+ * @note Read by the frontend only when \c caps has the
+ * \c RETRO_CAMERA_BUFFER_PLANAR bit, so a core built against an
+ * older header, whose struct ends above, is never read past.
+ * If \c NULL, this function will not be called.
+ */
+ retro_camera_frame_planar_t frame_planar;
};
/** @} */
@@ -7916,6 +8898,40 @@ struct retro_audio_sample_float_callback
retro_audio_sample_batch_float_t batch;
};
+/**
+ * Renders multiple audio frames of a multi-channel layout.
+ *
+ * Valid only after the frontend has answered \c true to
+ * \c RETRO_ENVIRONMENT_GET_AUDIO_SAMPLE_BATCH_MULTI.
+ *
+ * @param data Interleaved frames of \c channels samples, one sample
+ * a speaker in the ascending-bit order of \c layout; int16 or
+ * float in [-1.0, 1.0] by the entry point.
+ * @param frames The number of frames in \c data.
+ * @param channels Samples per frame: the bits set in \c layout.
+ * @param layout The speaker mask, from the \c RETRO_AUDIO_SPEAKER_ bits.
+ * @return The number of frames processed; 0 for a layout the frontend
+ * does not take.
+ * @see RETRO_ENVIRONMENT_GET_AUDIO_SAMPLE_BATCH_MULTI
+ */
+typedef size_t (RETRO_CALLCONV *retro_audio_sample_batch_multi_int16_t)(
+ const int16_t *data, size_t frames, unsigned channels, unsigned layout);
+typedef size_t (RETRO_CALLCONV *retro_audio_sample_batch_multi_float_t)(
+ const float *data, size_t frames, unsigned channels, unsigned layout);
+
+/**
+ * Multi-channel batch callbacks handed to the core in response to
+ * \c RETRO_ENVIRONMENT_GET_AUDIO_SAMPLE_BATCH_MULTI.
+ */
+struct retro_audio_sample_multi_callback
+{
+ /* Set by the frontend. */
+ retro_audio_sample_batch_multi_int16_t batch_int16;
+ /* Set by the frontend when float output is negotiated too, NULL
+ * otherwise. */
+ retro_audio_sample_batch_multi_float_t batch_float;
+};
+
/**
* Polls input.
*
diff --git a/Source/Core/AudioCommon/SoundStream.h b/Source/Core/AudioCommon/SoundStream.h
index f6993cdf0b..afc12f4b77 100644
--- a/Source/Core/AudioCommon/SoundStream.h
+++ b/Source/Core/AudioCommon/SoundStream.h
@@ -25,5 +25,7 @@ public:
#ifdef __LIBRETRO__
virtual void Update(unsigned int num_samples) {}
virtual void ProcessCallBack() {}
+ // On the CPU thread at each emulated field boundary
+ virtual void NewField() {}
#endif
};
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index cda66bffd1..4f70022221 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -1004,6 +1004,10 @@ void Callback_NewField(Core::System& system)
#ifdef __LIBRETRO__
if (system.IsDualCoreMode())
system.GetFifo().StopGpuLoop();
+ // frontend waiting on audio here holds the thread while
+ // the frame is presented.
+ if (SoundStream* sound_stream = system.GetSoundStream())
+ sound_stream->NewField();
#endif
}
diff --git a/Source/Core/DolphinLibretro/Audio.cpp b/Source/Core/DolphinLibretro/Audio.cpp
index a96698067d..04deefa3ea 100644
--- a/Source/Core/DolphinLibretro/Audio.cpp
+++ b/Source/Core/DolphinLibretro/Audio.cpp
@@ -24,7 +24,8 @@ enum CallBackMode
{
// Dolphin will Push samples into the soundstream
PUSH_SAMPLES = 0,
- // Samples are pushed per frame in retro_run, but use frame time callback to set pace
+ // One emulated frame's samples are pushed in each retro_run, so the frontend's audio sync and
+ // rate control pace the core
SYNC_PER_FRAME = 1,
// RetroArch requests audio samples based on its callbacks (target refresh rate/buffer status)
ASYNC_CALLBACK = 2,
@@ -77,7 +78,7 @@ unsigned int GetActiveSampleRate()
void Reset()
{
g_use_call_back_audio = Libretro::Options::GetCached(Libretro::Options::audio::CALL_BACK_AUDIO,
- CallBackMode::PUSH_SAMPLES);
+ CallBackMode::SYNC_PER_FRAME);
g_audio_state_cb = false;
}
@@ -89,6 +90,11 @@ void Init()
if (g_use_call_back_audio == CallBackMode::PUSH_SAMPLES)
return;
+ // a frontend audio callback would make the frontend treat the core's
+ // audio as callback driven, outside its audio sync and rate control
+ if (g_use_call_back_audio == CallBackMode::SYNC_PER_FRAME)
+ return;
+
retro_audio_callback racb = {};
racb.callback = &retroarch_audio_cb;
racb.set_state = &retroarch_audio_state_cb;
@@ -217,34 +223,29 @@ void Stream::MixAndPush(unsigned int num_samples)
}
}
+// Per frame sync: each field's samples go to the frontend from the CPU thread as the field ends,
+void Stream::NewField()
+{
+ PushAudioForFrame();
+}
+
void Stream::PushAudioForFrame()
{
if (g_use_call_back_audio != CallBackMode::SYNC_PER_FRAME || !m_mixer || !batch_cb)
return;
- // Calculate samples needed for this frame at output rate
- unsigned samples_for_frame;
-
- if (FrameTiming::IsEnabled())
- {
- samples_for_frame = GetSamplesForFrame(m_sample_rate);
- }
- else
- {
- if (Libretro::g_core_refresh_rate <= 0.0)
- {
- if (retro_get_region() == RETRO_REGION_NTSC)
- samples_for_frame = m_sample_rate / 60;
- else
- samples_for_frame = m_sample_rate / 50;
- }
- else
- {
- samples_for_frame =
- static_cast(std::lround(m_sample_rate / Libretro::g_core_refresh_rate));
- }
- }
-
+ // The DSP fills the mixer at the game's refresh rate, not the
+ // display's, so that rate is used and the fraction carried over: the mixer then neither fills up
+ // nor runs dry, and the frontend's rate control covers the display's difference.
+ double frame_rate = Libretro::g_core_refresh_rate;
+ if (frame_rate <= 0.0)
+ frame_rate = (retro_get_region() == RETRO_REGION_NTSC) ? 60.0 / 1.001 : 50.0;
+
+ static double carry = 0.0;
+ const double exact = m_sample_rate / frame_rate + carry;
+ unsigned samples_for_frame = static_cast(exact);
+ carry = exact - samples_for_frame;
+
samples_for_frame = std::clamp(samples_for_frame, MIN_SAMPLES, MAX_SAMPLES);
MixAndPush(samples_for_frame);
@@ -276,9 +277,10 @@ void Stream::ProcessCallBack()
if (Libretro::Audio::g_buf_support.load(std::memory_order_relaxed))
{
+ constexpr unsigned FULL_PERCENT = 75;
unsigned occ = Libretro::Audio::g_buf_occupancy.load(std::memory_order_relaxed);
- if (occ >= MAX_SAMPLES)
+ if (occ >= FULL_PERCENT)
return;
// Use frame time to decide how much to push
diff --git a/Source/Core/DolphinLibretro/Audio.h b/Source/Core/DolphinLibretro/Audio.h
index 44d3f7ca73..4aa4f140d1 100644
--- a/Source/Core/DolphinLibretro/Audio.h
+++ b/Source/Core/DolphinLibretro/Audio.h
@@ -39,6 +39,7 @@ public:
void Update(unsigned int num_samples) override;
void ProcessCallBack() override;
+ void NewField() override;
private:
s16 m_buffer[MAX_SAMPLES * 2];
diff --git a/Source/Core/DolphinLibretro/Common/Options.cpp b/Source/Core/DolphinLibretro/Common/Options.cpp
index 3dacadeda7..b757cb1e33 100644
--- a/Source/Core/DolphinLibretro/Common/Options.cpp
+++ b/Source/Core/DolphinLibretro/Common/Options.cpp
@@ -603,7 +603,7 @@ static struct retro_core_option_v2_definition option_defs[] = {
{ "2", "Async - driven by callbacks using refresh rate and audio buffer status" },
{ nullptr, nullptr }
},
- "0"
+ "1"
},
// ========== SYSCONF (GC) ==========
diff --git a/Source/Core/DolphinLibretro/Main.cpp b/Source/Core/DolphinLibretro/Main.cpp
index 09cbb1636c..1b05ed022f 100644
--- a/Source/Core/DolphinLibretro/Main.cpp
+++ b/Source/Core/DolphinLibretro/Main.cpp
@@ -500,15 +500,6 @@ void retro_run(void)
RETRO_PERFORMANCE_STOP(dolphin_main_func);
- if (auto* sound_stream = system.GetSoundStream())
- {
- auto* libretro_stream = static_cast(sound_stream);
- if (libretro_stream)
- {
- libretro_stream->PushAudioForFrame();
- }
- }
-
if (Libretro::Input::g_has_microphone_support)
poll_microphone();
}