diff --git a/source/frontends/libretro/libretro-common/include/libretro.h b/source/frontends/libretro/libretro-common/include/libretro.h
index 3def427e4..761bb5503 100644
--- a/source/frontends/libretro/libretro-common/include/libretro.h
+++ b/source/frontends/libretro/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.
@@ -2718,158 +2735,721 @@ 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
- * (30-bit) source surface end to end, i.e. whether a frame submitted as
- * #RETRO_PIXEL_FORMAT_XRGB2101010 reaches the display without the frontend
- * narrowing it to 8 bits per channel.
- *
- * Unlike SET_PIXEL_FORMAT, which accepts XRGB2101010 unconditionally and
- * transparently down-converts when the driver cannot present 10-bit, this
- * lets a core discover the real capability so it can avoid pointless work:
- * a core that has both a 10-bit and an 8-bit output path should prefer the
- * 8-bit path when this returns \c false, since emitting 10-bit only to have
- * the frontend narrow it wastes effort and, for content that starts at 8
- * bits, is a no-op round trip; going straight to 8 bits also rounds rather
- * than truncates.
+ * Queries the frontend's HDR presentation contract.
+ * Call from the emulation thread, as with video format negotiation.
+ * data points to struct retro_hdr_metadata. The caller zeroes it and sets
+ * version = 1 and size = sizeof(struct retro_hdr_metadata) before calling.
+ * Unknown versions and undersized buffers return false without modification.
+ * Larger buffers are accepted; bytes beyond the version-1 layout are untouched.
+ * Re-query after presentation or settings changes. This replaces experimental
+ * commands 88-92; those numbers must not be reused.
+ */
+#define RETRO_ENVIRONMENT_GET_HDR_METADATA (101 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/**
+ * Queries presentation state and optional display capabilities together.
+ * data points to struct retro_display_metadata, with version = 1 and size set
+ * by the caller as above. The frontend fills the nested HDR header itself.
+ * Unknown geometry/capabilities are zero. No raw EDID or display identity is
+ * exposed. The HDR block describes presentation, not panel capability.
+ */
+#define RETRO_ENVIRONMENT_GET_DISPLAY_METADATA (102 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
+/* Native core-input formats, independent of output mode or display bit depth. */
+#define RETRO_HDR_SOURCE_FORMAT_HDR10_2101010 (1u << 0)
+/* RETRO_PIXEL_FORMAT_SCRGB_RGBA16F core input.
+ * Do not advertise solely because an FP16 output framebuffer exists. */
+#define RETRO_HDR_SOURCE_FORMAT_SCRGB_RGBA16F (1u << 1)
+
+struct retro_hdr_metadata
+{
+ unsigned version;
+ unsigned size;
+ /* 0 = SDR or no active output, 1 = PQ Rec.2020, 2 = linear Rec.709. */
+ unsigned output_mode;
+ /* RETRO_HDR_SOURCE_FORMAT_* mask; zero means none known.
+ * Before video init this reports configured capability; SET_PIXEL_FORMAT
+ * must still accept the requested format. Ignore unknown bits. */
+ uint32_t source_formats;
+ float paper_white_nits;
+ /* Effective frontend target, including a display-peak override. */
+ float max_nits;
+ float min_nits;
+ /* SDR expansion: 0 accurate, 1 expanded, 2 P3, 3 unrotated. */
+ unsigned expand_gamut;
+ /* Content light levels, zero when unknown; never inferred from peak. */
+ float max_cll;
+ float max_fall;
+ /* H.273 codes; zero when unspecified. Linear transfer is 8, PQ is 16. */
+ unsigned color_primaries;
+ unsigned transfer_function;
+ /* For accepted HDR10_2101010 input: no creative curve, paper-white scale
+ * or SDR gamut expansion. PQ-to-linear/gamut conversion remains required
+ * for scRGB. Does not override a shader preset's existing contract. */
+ bool passthrough;
+ /* 0 = full-spectrum rational; other operators are not implemented. */
+ unsigned tonemap_operator;
+ uint32_t reserved[8];
+};
+
+struct retro_display_geometry
+{
+ unsigned native_width;
+ unsigned native_height;
+ unsigned width_mm;
+ unsigned height_mm;
+ unsigned refresh_min_mHz;
+ unsigned refresh_max_mHz;
+};
+
+struct retro_display_color_caps
+{
+ float peak_nits;
+ float min_nits;
+ float max_frame_avg_nits;
+ /* CIE xy / 10000: Rx,Ry,Gx,Gy,Bx,By,Wx,Wy. All zero = unknown. */
+ uint16_t primaries_xy[8];
+ /* Bit 0 SDR, bit 1 PQ, bit 2 HLG. Zero = unknown. */
+ unsigned supported_eotf_mask;
+ unsigned edid_gamma_x100;
+ /* 0 unknown, 1 user, 2 EDID/CTA, 3 platform API. */
+ unsigned capability_source;
+};
+
+struct retro_display_metadata
+{
+ unsigned version;
+ unsigned size;
+ struct retro_display_geometry geo;
+ struct retro_hdr_metadata hdr;
+ struct retro_display_color_caps caps;
+};
+
+/**
+ * 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.
*
- * The result may change across a driver reinit (e.g. the user switches
- * video driver or toggles HDR), so a core that cares should query it when
- * (re)choosing its pixel format rather than caching it indefinitely.
+ * 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 bool *.
- * Set to \c true if a 10-bit source surface is presented natively, \c false
- * if XRGB2101010 frames are down-converted to 8-bit.
- * @return \c true if the environment call is recognised (the value at
- * \c data is then valid), \c false if it is unsupported (an older frontend);
- * a core must treat "unsupported" as "no guarantee of native 10-bit".
+ * @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.
*/
-#define RETRO_ENVIRONMENT_GET_SCREEN_10BPC_CAPABLE (88 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+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 */
/**
- * Queries the luminance, in nits, that the frontend treats as SDR paper
- * white when presenting HDR content.
+ * 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.
*
- * Only meaningful together with #RETRO_PIXEL_FORMAT_HDR10_2101010: a core
- * encoding absolute luminance itself has to know where the user expects
- * ordinary, non-glowing image content to sit, otherwise the whole frame is
- * either dim or searing. The value corresponds to the frontend's HDR paper
- * white setting; typical values are 100-400.
+ * 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 user can change it at any time, so a core should re-query it when it
- * rebuilds its colour tables rather than caching it forever.
+ * 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 float *.
- * Set to the paper white luminance in nits.
- * @return \c true if the call is recognised and the value is valid, \c false
- * on a frontend that does not implement it; callers should then assume a
- * sensible default (200 nits).
+ * @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_HDR_PAPER_WHITE_NITS (89 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+#define RETRO_ENVIRONMENT_GET_VR_FRAME_STATE (97 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+
/**
- * Queries the colour-gamut treatment the frontend applies to SDR content
- * when presenting HDR.
- *
- * Only meaningful together with #RETRO_PIXEL_FORMAT_HDR10_2101010. A core
- * emitting that format performs its own Rec.709 -> Rec.2020 rotation, and
- * has to make the same choice the frontend makes for SDR content, or a
- * scene will change saturation when the user switches the core between an
- * SDR format and HDR10. The values match the frontend's "Colour Boost"
- * setting:
+ * 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).
*
- * 0 Accurate -- proper Rec.709 -> Rec.2020 conversion, no boost
- * 1 Expanded -- Rec.709 -> a slightly wider space
- * 2 Wide -- Rec.709 -> DCI-P3
- * 3 Super -- no rotation at all; values stay Rec.709 and the boost
- * comes from the display interpreting them as Rec.2020
+ * position and orientation describe the eye-to-tracking-space transform,
+ * not its inverse.
*
- * A core should re-query this when it rebuilds its colour tables, since the
- * user can change it at any time.
+ * 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).
*
- * @param[out] data unsigned *.
- * Set to one of the values above.
- * @return \c true if the call is recognised, \c false on a frontend that
- * does not implement it; callers should then assume 0 (Accurate).
+ * @see RETRO_ENVIRONMENT_GET_VR_FRAME_STATE
*/
-#define RETRO_ENVIRONMENT_GET_HDR_EXPAND_GAMUT (90 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+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;
+};
/**
- * Queries which HDR output mode the frontend is presenting with.
- *
- * Only meaningful together with #RETRO_PIXEL_FORMAT_HDR10_2101010. Both
- * output modes accept the same PQ Rec.2020 frame, but they do not treat its
- * primaries identically: an HDR10 swapchain presents the samples as-is,
- * while an scRGB swapchain converts them and applies a Rec.2020 -> Rec.709
- * rotation on the way. A core that encodes its own gamut -- which it must,
- * to honour #RETRO_ENVIRONMENT_GET_HDR_EXPAND_GAMUT -- therefore has to know
- * which of the two it is feeding, or its colour choice is undone by that
- * rotation on one of them.
+ * Retrieves the current head-tracked pose.
*
- * Values:
- * 0 HDR output is off
- * 1 HDR10 (PQ, Rec.2020 swapchain); samples are presented unchanged
- * 2 scRGB (linear FP16, Rec.709); the frontend applies Rec.2020 ->
- * Rec.709 to the decoded samples
+ * 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.
+ * Versions 1, 2 and 3 are supported. Initialize the version before querying.
+ * Version 1 writes only the original prefix through close. 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 3
+#define RETRO_VIDEO_DECODE_DEFER_PLANES (1u << 0)
+
+struct retro_video_decode_frame
+{
+ const uint8_t *planes[3];
+ unsigned strides[3];
+ unsigned width, height;
+ bool hardware;
+};
+
+enum retro_video_decode_gpu_api
+{
+ RETRO_VIDEO_DECODE_GPU_VULKAN = 1,
+ RETRO_VIDEO_DECODE_GPU_D3D11 = 2
+};
+
+/* Version 3 GPU output. Vulkan uses same-device VkImage/views and timeline
+ * semaphores; D3D11 uses shared NT handles. Wait on wait_value before GPU
+ * reads, preserve layout, and signal done_value after the final GPU read.
+ * An unused lease is returned with abandon. Close/context destruction
+ * requires all leases to have completed; handles must never be cached
+ * beyond their lease. Chroma views are R8/R8G8 (8-bit 4:2:0).
+ * The copy is independent of subsequent decode/drain output. */
+struct retro_video_decode_gpu_frame
+{
+ uint64_t image, plane_views[2];
+ uint64_t wait, wait_value, done, done_value;
+ void (*abandon)(void *owner, uint64_t done, uint64_t done_value);
+ void *owner;
+ unsigned width, height, layout, yuv, pixfmt, array_slice, crop_x, crop_y;
+ unsigned api;
+};
+
+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);
+ /* Version 2: flags=0 preserves eager CPU planes. DEFER_PLANES returns
+ * width/height/hardware with null planes; get_frame materializes them.
+ * Output expires on the next decode/drain/close. No GPU handle is leased.
+ * 2: hardware busy; retry decode_ex with the same live input pointer
+ * and size, or retry drain_ex later. Do not consume another access unit.
+ * Unsupported flag bits fail without consuming input. */
+ int (*decode_ex)(void *decoder, const uint8_t *data, size_t size,
+ struct retro_video_decode_frame *frame, unsigned flags);
+ int (*drain_ex)(void *decoder, struct retro_video_decode_frame *frame,
+ unsigned flags);
+ /* 1: CPU planes ready; -1: no current output or readback failure.
+ * Repeated calls reuse the same CPU copy. */
+ int (*get_frame)(void *decoder, struct retro_video_decode_frame *frame);
+ /* Version 3: export the current deferred output once. 1 leased,
+ * 2 busy (retry later), -1 unavailable/error. CPU get_frame remains
+ * valid until decode/drain. Failed calls acquire no lease. */
+ int (*get_gpu_frame)(void *decoder, struct retro_video_decode_gpu_frame *frame);
+};
+
+
+
+#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).
*
- * The user can switch this at any time, so a core should re-query it when it
- * rebuilds its colour tables rather than caching it indefinitely.
+ * position and orientation describe the head-to-tracking-space transform,
+ * not its inverse. The orientation quaternion rotates vectors from head
+ * space into tracking space.
*
- * @param[out] data unsigned *.
- * Set to one of the values above.
- * @return \c true if the call is recognised, \c false on a frontend that
- * does not implement it; callers should then assume 1 (HDR10), which is the
- * mode that needs no compensation.
+ * 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.
*/
-#define RETRO_ENVIRONMENT_GET_HDR_OUTPUT_MODE (91 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+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;
+};
/**
- * Queries the peak luminance the frontend is presenting to, in cd/m2 (nits).
+ * Retrieves where the user's hands are: each motion controller's poses
+ * and each hand's joints.
*
- * Only meaningful together with #RETRO_PIXEL_FORMAT_HDR10_2101010. A core
- * emitting HDR10 encodes absolute luminance itself, so it decides where its
- * brightest content lands. #RETRO_ENVIRONMENT_GET_HDR_PAPER_WHITE_NITS says
- * where ordinary content sits; this says how much room there is above it. The
- * gap between the two is the entire headroom a core has for highlights, and
- * without it a core has to guess - a guess that is too dark on a bright display
- * and clips on a dim one.
+ * A core reads a pose, a joint or a field only when its flag says so.
*
- * This is what the *display* can do, not what the content wants, so it is a
- * ceiling to roll off toward rather than a level to target. A core should not
- * assume anything it emits below this value is reproduced exactly; displays
- * tone map internally, and many report a peak they can only hold over a small
- * window.
+ * 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.
*
- * May be lower than paper white if the user has configured it so. A core
- * should treat that as zero headroom and clamp to paper white rather than
- * producing a negative range.
+ * 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().
*
- * The user can change this at any time, so a core should re-query it rather
- * than caching it indefinitely.
+ * @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.
*
- * @param[out] data float *.
- * Set to the peak luminance in nits.
- * @return \c true if the call is recognised, \c false on a frontend that does
- * not implement it; callers should then fall back to a sensible default.
- * 1000 nits is a reasonable assumption, being both the HDR10 reference peak
- * and roughly what mid-range HDR displays achieve.
+ * 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.
*/
-#define RETRO_ENVIRONMENT_GET_HDR_MAX_NITS (92 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+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.
@@ -3052,8 +3632,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.
*
@@ -3245,6 +3866,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.
*
@@ -3317,6 +4056,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.
*
@@ -3404,6 +4166,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;
};
/**
@@ -3496,7 +4280,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. */
@@ -3755,6 +4542,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.
@@ -5050,6 +5851,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.
@@ -5057,6 +5871,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,
@@ -5135,6 +5987,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.
*
@@ -5210,6 +6073,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;
};
/** @} */
@@ -6108,19 +6980,42 @@ enum retro_pixel_format
* A frontend that accepts this format MUST pass the samples through to an
* HDR10 (PQ / Rec.2020) swapchain without re-encoding them: no inverse
* tonemap, no Rec.709->Rec.2020 rotation, no paper-white scaling, since
- * the core has already applied all of it. A frontend that cannot present
- * HDR10 natively must reject the format from SET_PIXEL_FORMAT rather than
+ * the core has already applied all of it. For scRGB output the frontend must instead decode PQ, rotate Rec.2020
+ * to linear Rec.709 and scale to the platform reference white, without
+ * a second creative curve. A frontend without either path must reject the format from SET_PIXEL_FORMAT rather than
* silently down-converting -- PQ samples interpreted as SDR look badly
* wrong, so the usual transparent narrowing is not safe here. Cores
* should therefore keep an SDR path and fall back when this is refused.
*
- * Cores should query #RETRO_ENVIRONMENT_GET_HDR_PAPER_WHITE_NITS to learn
+ * Cores should query #RETRO_ENVIRONMENT_GET_HDR_METADATA to learn
* the luminance the user considers "SDR white" and map their normal
* output to it; content authored above that value is what produces the
* HDR effect.
*/
RETRO_PIXEL_FORMAT_HDR10_2101010 = 4,
+ /**
+ * FP16 linear scRGB with Rec.709/sRGB primaries and D65 white.
+ *
+ * Each pixel is four IEEE 754 binary16 samples in memory order R, G, B, A,
+ * with each 16-bit sample in native byte order. Alpha is ignored; cores
+ * should set it to 1.0. Each pixel occupies 8 bytes; pitch is in bytes.
+ * RGB samples must be finite. Negative values and values above 1.0 are
+ * valid and must not be clamped during upload or intermediate storage.
+ *
+ * RGB 1.0 represents 80 cd/m^2, independent of frontend paper white and
+ * platform output units. There is no gamma/PQ transfer to decode. Cores
+ * can use GET_HDR_METADATA paper_white_nits / 80.0 for their SDR white.
+ *
+ * An accepting frontend must preserve linear HDR values, converting
+ * primaries, transfer and reference-white units on the GPU as required
+ * by its output. Do not apply SDR expansion or a second paper-white
+ * scale. Unsupported paths must reject SET_PIXEL_FORMAT; cores must
+ * check its result and retain an SDR fallback. The corresponding
+ * source_formats bit is RETRO_HDR_SOURCE_FORMAT_SCRGB_RGBA16F.
+ */
+ RETRO_PIXEL_FORMAT_SCRGB_RGBA16F = 5,
+
/**
* @private Defined to ensure that sizeof(retro_pixel_format) == sizeof(int).
* Do not use.
@@ -8021,6 +8916,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/frontends/qt/QHexView b/source/frontends/qt/QHexView
index 3d433cff3..889388135 160000
--- a/source/frontends/qt/QHexView
+++ b/source/frontends/qt/QHexView
@@ -1 +1 @@
-Subproject commit 3d433cff336c1d1218397ed40f287be7249e9b66
+Subproject commit 8893881350ea1d0bb0deafb7ae4a700e8de6683f
diff --git a/source/frontends/sdl/imgui/imgui b/source/frontends/sdl/imgui/imgui
index f1cc2ae15..c1aaac65e 160000
--- a/source/frontends/sdl/imgui/imgui
+++ b/source/frontends/sdl/imgui/imgui
@@ -1 +1 @@
-Subproject commit f1cc2ae15e53a861a874c3034aae6798fde194ab
+Subproject commit c1aaac65e896a45f81f47a4d68f9959e0f86c859